org.springframework.integration.support.management.ExponentialMovingAverage.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.support.management.ExponentialMovingAverage.java

Source

/*
 * Copyright 2009-2019 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
 *
 *      https://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.integration.support.management;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

/**
 * Cumulative statistics for a series of real numbers with higher weight given to recent data.
 * Clients call {@link #append(double)} every time there is a new measurement, and then can collect summary
 * statistics from the convenience getters (e.g. {@link #getStatistics()}). Older values are given exponentially smaller
 * weight, with a decay factor determined by a "window" size chosen by the caller. The result is a good approximation to
 * the statistics of the series but with more weight given to recent measurements, so if the statistics change over time
 * those trends can be approximately reflected. For performance reasons, the calculation is performed on retrieval,
 * {@code window * 5} samples are retained meaning that the earliest retained value contributes just 0.5% to the
 * sum.
 * @deprecated in favor of dimensional metrics via
 * {@link org.springframework.integration.support.management.metrics.MeterFacade}.
 * Built-in metrics will be removed in a future release.
 *
 * @author Dave Syer
 * @author Gary Russell
 * @since 2.0
 */
@Deprecated
public class ExponentialMovingAverage {

    private volatile long count;

    private volatile double min = Double.MAX_VALUE;

    private volatile double max;

    private final Deque<Double> samples = new ArrayDeque<>();

    private final int retention;

    private final int window;

    private final double factor;

    /**
     * Create a moving average accumulator with decay lapse window provided. Measurements older than this will have
     * smaller weight than <code>1/e</code>.
     * @param window the exponential lapse window (number of measurements)
     */
    public ExponentialMovingAverage(int window) {
        this(window, 1);
    }

    /**
     * Create a moving average accumulator with decay lapse window provided. Measurements older than this will have
     * smaller weight than <code>1/e</code>.
     * @param window the exponential lapse window (number of measurements)
     * @param factor a factor by which raw values are reduced during analysis; e.g. to analyze in ms and
     * raw values are ns, set the factor to 1000000.0.
     * @since 4.2
     */
    public ExponentialMovingAverage(int window, double factor) {
        this.window = window;
        this.retention = window * 5; // last retained value contributes just 0.5% to the sum
        this.factor = factor;
    }

    public synchronized void reset() {
        this.count = 0;
        this.min = Double.MAX_VALUE;
        this.max = 0;
        this.samples.clear();
    }

    /**
     * Add a new measurement to the series.
     * @param value the measurement to append
     */
    public synchronized void append(double value) {
        if (this.samples.size() == this.retention) {
            this.samples.poll();
        }
        this.samples.add(value);
        this.count++; //NOSONAR - false positive, we're synchronized
    }

    private Statistics calc() {
        List<Double> copy;
        long currentCount;
        synchronized (this) {
            copy = new ArrayList<Double>(this.samples);
            currentCount = this.count;
        }
        double sum = 0;
        double decay = 1 - 1. / this.window;
        double sumSquares = 0;
        double weight = 0;
        double currentMin = this.min;
        double currentMax = this.max;
        for (Double value : copy) {
            value /= this.factor;
            if (value > currentMax) {
                currentMax = value;
            }
            if (value < currentMin) {
                currentMin = value;
            }
            sum = decay * sum + value;
            sumSquares = decay * sumSquares + value * value;
            weight = decay * weight + 1;
        }
        synchronized (this) {
            if (currentMax > this.max) {
                this.max = currentMax;
            }
            if (currentMin < this.min) {
                this.min = currentMin;
            }
        }
        double mean = weight > 0 ? sum / weight : 0.;
        double var = weight > 0 ? sumSquares / weight - mean * mean : 0.;
        double standardDeviation = var > 0 ? Math.sqrt(var) : 0;
        return new Statistics(currentCount, currentMin == Double.MAX_VALUE ? 0 : currentMin, currentMax, mean,
                standardDeviation); //NOSONAR
    }

    /**
     * @return the number of measurements recorded
     */
    public int getCount() {
        return (int) this.count;
    }

    /**
     * @return the number of measurements recorded
     */
    public long getCountLong() {
        return this.count;
    }

    /**
     * @return the mean value
     */
    public double getMean() {
        return calc().getMean();
    }

    /**
     * @return the approximate standard deviation
     */
    public double getStandardDeviation() {
        return calc().getStandardDeviation();
    }

    /**
     * @return the maximum value recorded (not weighted)
     */
    public double getMax() {
        return calc().getMax();
    }

    /**
     * @return the minimum value recorded (not weighted)
     */
    public double getMin() {
        return calc().getMin();
    }

    /**
     * @return summary statistics (count, mean, standard deviation etc.)
     */
    public Statistics getStatistics() {
        return calc();
    }

    @Override
    public String toString() {
        return getStatistics().toString();
    }

}