org.springframework.xd.analytics.metrics.memory.InMemoryAggregateCounter.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.xd.analytics.metrics.memory.InMemoryAggregateCounter.java

Source

/*
 * Copyright 2002-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.xd.analytics.metrics.memory;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.joda.time.Chronology;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.Duration;
import org.joda.time.Interval;

import org.joda.time.Months;
import org.joda.time.Years;
import org.springframework.util.Assert;
import org.springframework.xd.analytics.metrics.core.AggregateCount;
import org.springframework.xd.analytics.metrics.core.AggregateCountResolution;
import org.springframework.xd.analytics.metrics.core.Counter;
import org.springframework.xd.analytics.metrics.core.MetricUtils;

/**
 * A counter that tracks integral values but also remembers how its value was distributed over time.
 * 
 * <p>
 * This core class only holds data structures. Depending on backing stores, logic for computing totals may be
 * implemented in a specialization of this class or at the repository level.
 * </p>
 * 
 * @author Luke Taylor
 * @author Eric Bottard
 */
class InMemoryAggregateCounter extends Counter {
    private Map<Integer, long[]> monthCountsByYear = new HashMap<Integer, long[]>();

    private Map<Integer, long[]> dayCountsByYear = new HashMap<Integer, long[]>();

    private Map<Integer, long[]> hourCountsByDay = new HashMap<Integer, long[]>();

    private Map<Integer, long[]> minuteCountsByDay = new HashMap<Integer, long[]>();

    public InMemoryAggregateCounter(String name, long value) {
        super(name, value);
    }

    public InMemoryAggregateCounter(String name) {
        super(name);
    }

    public AggregateCount getCounts(int nCounts, DateTime endDate, AggregateCountResolution resolution) {
        Assert.notNull(endDate, "endDate must not be null");

        return getCounts(new Interval(resolution.minus(endDate, nCounts - 1), endDate), resolution);
    }

    public AggregateCount getCounts(Interval interval, AggregateCountResolution resolution) {
        DateTime start = interval.getStart();
        DateTime end = interval.getEnd();
        Chronology c = interval.getChronology();

        long[] counts;
        if (resolution == AggregateCountResolution.minute) {
            List<long[]> days = accumulateDayCounts(minuteCountsByDay, start, end, 60 * 24);

            counts = MetricUtils.concatArrays(days, interval.getStart().getMinuteOfDay(),
                    interval.toPeriod().toStandardMinutes().getMinutes() + 1);
        } else if (resolution == AggregateCountResolution.hour) {
            List<long[]> days = accumulateDayCounts(hourCountsByDay, start, end, 24);

            counts = MetricUtils.concatArrays(days, interval.getStart().getHourOfDay(),
                    interval.toPeriod().toStandardHours().getHours() + 1);
        } else if (resolution == AggregateCountResolution.day) {
            DateTime startDay = new DateTime(c.dayOfYear().roundFloor(start.getMillis()));
            DateTime endDay = new DateTime(c.dayOfYear().roundFloor(end.plusDays(1).getMillis()));
            int nDays = Days.daysBetween(startDay, endDay).getDays();
            DateTime cursor = new DateTime(c.year().roundFloor(interval.getStartMillis()));
            List<long[]> yearDays = new ArrayList<long[]>();
            DateTime endYear = new DateTime(c.year().roundCeiling(end.getMillis()));

            while (cursor.isBefore(endYear)) {
                long[] dayCounts = dayCountsByYear.get(cursor.getYear());
                if (dayCounts == null) {
                    // Querying where we have no data
                    dayCounts = new long[daysInYear(cursor.getYear())];
                }
                yearDays.add(dayCounts);
                cursor = cursor.plusYears(1);
            }

            counts = MetricUtils.concatArrays(yearDays, startDay.getDayOfYear() - 1, nDays);

        } else if (resolution == AggregateCountResolution.month) {
            DateTime startMonth = new DateTime(c.monthOfYear().roundFloor(interval.getStartMillis()));
            DateTime endMonth = new DateTime(c.monthOfYear().roundFloor(end.plusMonths(1).getMillis()));
            int nMonths = Months.monthsBetween(startMonth, endMonth).getMonths();
            DateTime cursor = new DateTime(c.year().roundFloor(interval.getStartMillis()));
            List<long[]> yearMonths = new ArrayList<long[]>();
            DateTime endYear = new DateTime(c.year().roundCeiling(end.getMillis()));

            while (cursor.isBefore(endYear)) {
                long[] monthCounts = monthCountsByYear.get(cursor.getYear());
                if (monthCounts == null) {
                    monthCounts = new long[12];
                }
                yearMonths.add(monthCounts);
                cursor = cursor.plusYears(1);
            }

            counts = MetricUtils.concatArrays(yearMonths, startMonth.getMonthOfYear() - 1, nMonths);
        } else if (resolution == AggregateCountResolution.year) {
            DateTime startYear = new DateTime(interval.getStart().getYear(), 1, 1, 0, 0);
            DateTime endYear = new DateTime(end.getYear() + 1, 1, 1, 0, 0);
            int nYears = Years.yearsBetween(startYear, endYear).getYears();
            counts = new long[nYears];

            for (int i = 0; i < nYears; i++) {
                long[] monthCounts = monthCountsByYear.get(startYear.plusYears(i).getYear());
                counts[i] = MetricUtils.sum(monthCounts);
            }

        } else {
            throw new IllegalStateException("Shouldn't happen. Unhandled resolution: " + resolution);
        }
        return new AggregateCount(getName(), interval, counts, resolution);
    }

    private static List<long[]> accumulateDayCounts(Map<Integer, long[]> fromDayCounts, DateTime start,
            DateTime end, int subSize) {
        List<long[]> days = new ArrayList<long[]>();
        Duration step = Duration.standardDays(1);
        long[] emptySubArray = new long[subSize];
        end = end.plusDays(1); // Need to account for an interval which crosses days

        for (DateTime now = start; now.isBefore(end); now = now.plus(step)) {
            int countsByDayKey = now.getYear() * 1000 + now.getDayOfYear();
            long[] dayCounts = fromDayCounts.get(countsByDayKey);

            if (dayCounts == null) {
                // Use an empty array if we don't have data
                dayCounts = emptySubArray;
            }
            days.add(dayCounts);
        }
        return days;
    }

    private int daysInYear(int year) {
        Duration d = new Duration(new DateTime(year, 1, 1, 0, 0), new DateTime(year + 1, 1, 1, 0, 0));
        return (int) d.getStandardDays();
    }

    synchronized long increment(long amount, DateTime dateTime) {
        int year = dateTime.getYear();
        int month = dateTime.getMonthOfYear();
        int day = dateTime.getDayOfYear();
        int hour = dateTime.getHourOfDay();
        int minute = dateTime.getMinuteOfDay();

        long[] monthCounts = monthCountsByYear.get(year);
        long[] dayCounts = dayCountsByYear.get(year);

        if (monthCounts == null) {
            monthCounts = new long[12];
            monthCountsByYear.put(year, monthCounts);
            dayCounts = new long[daysInYear(year)];
            dayCountsByYear.put(year, dayCounts);
        }

        int countsByDayKey = year * 1000 + day;
        long[] hourCounts = hourCountsByDay.get(countsByDayKey);

        if (hourCounts == null) {
            hourCounts = new long[24];
            hourCountsByDay.put(countsByDayKey, hourCounts);
        }

        long[] minuteCounts = minuteCountsByDay.get(countsByDayKey);

        if (minuteCounts == null) {
            minuteCounts = new long[60 * 24];
            minuteCountsByDay.put(countsByDayKey, minuteCounts);
        }

        minuteCounts[minute] += amount;
        monthCounts[month - 1] += amount;
        dayCounts[day - 1] += amount;
        hourCounts[hour] += amount;

        return increment(amount);
    }

}