@GET("/backend/aggregate/points")
  String getPoints(String content) throws IOException {
    logger.debug("getPoints(): content={}", content);
    PointsRequest request = ObjectMappers.readRequiredValue(mapper, content, PointsRequest.class);
    List<AggregatePoint> points = aggregateDao.readAggregates(request.getFrom(), request.getTo());

    StringBuilder sb = new StringBuilder();
    JsonGenerator jg = mapper.getFactory().createGenerator(CharStreams.asWriter(sb));
    jg.writeStartObject();
    jg.writeArrayFieldStart("points");
    for (AggregatePoint point : points) {
      jg.writeStartArray();
      jg.writeNumber(point.getCaptureTime());
      long durationAverage;
      if (point.getTraceCount() == 0) {
        durationAverage = 0;
      } else {
        durationAverage = point.getDurationTotal() / point.getTraceCount();
      }
      jg.writeNumber(durationAverage / 1000000000.0);
      jg.writeNumber(point.getTraceCount());
      jg.writeEndArray();
    }
    jg.writeEndArray();
    jg.writeNumberField("fixedAggregateIntervalSeconds", fixedAggregateIntervalSeconds);
    jg.writeEndObject();
    jg.close();
    return sb.toString();
  }
 @GET("/backend/aggregate/groupings")
 String getGroupings(String content) throws IOException {
   logger.debug("getGroupings(): content={}", content);
   GroupingsRequest request =
       ObjectMappers.readRequiredValue(mapper, content, GroupingsRequest.class);
   List<GroupingAggregate> groupings =
       aggregateDao.readGroupingAggregates(request.getFrom(), request.getTo(), request.getLimit());
   return mapper.writeValueAsString(groupings);
 }
/**
 * Json service to read aggregate data, bound to /backend/aggregate.
 *
 * @author Trask Stalnaker
 * @since 0.5
 */
@Singleton
@JsonService
class AggregateJsonService {

  private static final Logger logger = LoggerFactory.getLogger(AggregateJsonService.class);
  @ReadOnly private static final ObjectMapper mapper = ObjectMappers.create();

  private final AggregateDao aggregateDao;

  private final long fixedAggregateIntervalSeconds;

  AggregateJsonService(AggregateDao aggregateDao, long fixedAggregateIntervalSeconds) {
    this.aggregateDao = aggregateDao;
    this.fixedAggregateIntervalSeconds = fixedAggregateIntervalSeconds;
  }

  @GET("/backend/aggregate/points")
  String getPoints(String content) throws IOException {
    logger.debug("getPoints(): content={}", content);
    PointsRequest request = ObjectMappers.readRequiredValue(mapper, content, PointsRequest.class);
    List<AggregatePoint> points = aggregateDao.readAggregates(request.getFrom(), request.getTo());

    StringBuilder sb = new StringBuilder();
    JsonGenerator jg = mapper.getFactory().createGenerator(CharStreams.asWriter(sb));
    jg.writeStartObject();
    jg.writeArrayFieldStart("points");
    for (AggregatePoint point : points) {
      jg.writeStartArray();
      jg.writeNumber(point.getCaptureTime());
      long durationAverage;
      if (point.getTraceCount() == 0) {
        durationAverage = 0;
      } else {
        durationAverage = point.getDurationTotal() / point.getTraceCount();
      }
      jg.writeNumber(durationAverage / 1000000000.0);
      jg.writeNumber(point.getTraceCount());
      jg.writeEndArray();
    }
    jg.writeEndArray();
    jg.writeNumberField("fixedAggregateIntervalSeconds", fixedAggregateIntervalSeconds);
    jg.writeEndObject();
    jg.close();
    return sb.toString();
  }

  @GET("/backend/aggregate/groupings")
  String getGroupings(String content) throws IOException {
    logger.debug("getGroupings(): content={}", content);
    GroupingsRequest request =
        ObjectMappers.readRequiredValue(mapper, content, GroupingsRequest.class);
    List<GroupingAggregate> groupings =
        aggregateDao.readGroupingAggregates(request.getFrom(), request.getTo(), request.getLimit());
    return mapper.writeValueAsString(groupings);
  }

  private static class PointsRequest {

    private final long from;
    private final long to;

    @JsonCreator
    PointsRequest(@JsonProperty("from") @Nullable Long from, @JsonProperty("to") @Nullable Long to)
        throws JsonMappingException {
      checkRequiredProperty(from, "from");
      checkRequiredProperty(to, "to");
      this.from = from;
      this.to = to;
    }

    private long getFrom() {
      return from;
    }

    private long getTo() {
      return to;
    }
  }

  private static class GroupingsRequest {

    private final long from;
    private final long to;
    private final int limit;

    @JsonCreator
    GroupingsRequest(
        @JsonProperty("from") @Nullable Long from,
        @JsonProperty("to") @Nullable Long to,
        @JsonProperty("limit") @Nullable Integer limit)
        throws JsonMappingException {
      checkRequiredProperty(from, "from");
      checkRequiredProperty(to, "to");
      checkRequiredProperty(limit, "limit");
      this.from = from;
      this.to = to;
      this.limit = limit;
    }

    private long getFrom() {
      return from;
    }

    private long getTo() {
      return to;
    }

    public int getLimit() {
      return limit;
    }
  }
}