class MetricsRequestHelper {

  public TimelineMetrics fetchTimelineMetrics(URIBuilder uriBuilder, Long startTime, Long endTime) throws IOException {
    LOG.debug("Metrics request url = {}", uriBuilder);
    BufferedReader reader = null;
    TimelineMetrics timelineMetrics = null;
    try {

      HttpURLConnection connection = streamProvider.processURL(uriBuilder.toString(), HttpMethod.GET,
        (String) null, Collections.emptyMap());

      if (!checkConnectionForPrecisionException(connection)) {
        //Try one more time with higher precision
        String higherPrecision = getHigherPrecision(uriBuilder, startTime, endTime);
        if (higherPrecision != null) {
          LOG.debug("Requesting metrics with higher precision : {}", higherPrecision);
          uriBuilder.setParameter("precision", higherPrecision);
          String newSpec = uriBuilder.toString();
          connection = streamProvider.processURL(newSpec, HttpMethod.GET, (String) null,
            Collections.<String, List<String>>  emptyMap());
          if (!checkConnectionForPrecisionException(connection)) {
            throw new IOException("Encountered Precision exception : Higher precision request also failed.");
          }
        } else {
          throw new IOException("Encountered Precision exception : Unable to request higher precision");
        }
      }

      InputStream inputStream = connection.getInputStream();
      reader = new BufferedReader(new InputStreamReader(inputStream));
      timelineMetrics = timelineObjectReader.readValue(reader);

      if (LOG.isTraceEnabled()) {
        for (TimelineMetric metric : timelineMetrics.getMetrics()) {
          LOG.trace("metric: {}, size = {}, host = {}, app = {}, instance = {}, startTime = {}",
            metric.getMetricName(), metric.getMetricValues().size(), metric.getHostName(), metric.getAppId(), metric.getInstanceId(),
            new Date(metric.getStartTime()));
        }
      }
    } catch (IOException io) {
      String errorMsg = "Error getting timeline metrics : " + io.getMessage();
      LOG.error(errorMsg);
      if (LOG.isDebugEnabled()) {
        LOG.debug(errorMsg, io);
      }

      if (io instanceof SocketTimeoutException || io instanceof ConnectException) {
        errorMsg = "Cannot connect to collector: SocketTimeoutException for " + uriBuilder.getHost();
        LOG.error(errorMsg);
        throw io;
      }
    } catch (URISyntaxException e) {
      String errorMsg = "Error getting timeline metrics : " + e.getMessage();
      LOG.error(errorMsg);
      if (LOG.isDebugEnabled()) {
        LOG.debug(errorMsg, e);
      }
    } finally {
      if (reader != null) {
        try {
          reader.close();
        } catch (IOException e) {
          if (LOG.isWarnEnabled()) {
            if (LOG.isDebugEnabled()) {
              LOG.warn("Unable to close http input stream : spec=" + uriBuilder, e);
            } else {
              LOG.warn("Unable to close http input stream : spec=" + uriBuilder);
            }
          }
        }
      }
    }
    return timelineMetrics;
  }

  public  MetricsRequestHelper(URLStreamProvider streamProvider);

}