Index: jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesRecorder.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesRecorder.java (revision 6e582e4870279ca8c93f434e7a350c5db1d143b1) +++ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesRecorder.java (revision ) @@ -16,10 +16,14 @@ */ package org.apache.jackrabbit.stats; +import static java.lang.Math.round; + +import static java.util.Arrays.fill; + import java.util.concurrent.atomic.AtomicLong; -import org.apache.jackrabbit.api.stats.TimeSeries; import org.apache.jackrabbit.api.stats.RepositoryStatistics.Type; +import org.apache.jackrabbit.api.stats.TimeSeries; /** * Recorder of a time series. An instance of this class records (and clears) @@ -30,43 +34,61 @@ public class TimeSeriesRecorder implements TimeSeries { /** Value */ - private final AtomicLong counter = new AtomicLong(); + private final AtomicLong counter; /** Whether to reset value each second */ private final boolean resetValueEachSecond; + private final long defaultValue; + /** Measured value per second over the last minute. */ - private final long[] valuePerSecond = new long[60]; + private final long[] valuePerSecond; /** Measured value per minute over the last hour. */ - private final long[] valuePerMinute = new long[60]; + private final long[] valuePerMinute; /** Measured value per hour over the last week. */ - private final long[] valuePerHour = new long[7 * 24]; + private final long[] valuePerHour; /** Measured value per week over the last three years. */ - private final long[] valuePerWeek = new long[3 * 52]; + private final long[] valuePerWeek; /** Current second (index in {@link #valuePerSecond}) */ - private int seconds = 0; + private int seconds; /** Current minute (index in {@link #valuePerMinute}) */ - private int minutes = 0; + private int minutes; /** Current hour (index in {@link #valuePerHour}) */ - private int hours = 0; + private int hours; /** Current week (index in {@link #valuePerWeek}) */ - private int weeks = 0; + private int weeks; public TimeSeriesRecorder(Type type) { this(type.isResetValueEachSecond()); } public TimeSeriesRecorder(boolean resetValueEachSecond) { + this(resetValueEachSecond, 0); + } + + public TimeSeriesRecorder(boolean resetValueEachSecond, long defaultValue) { this.resetValueEachSecond = resetValueEachSecond; + this.defaultValue = defaultValue; + counter = new AtomicLong(defaultValue); + valuePerSecond = newArray(60, defaultValue); + valuePerMinute = newArray(60, defaultValue); + valuePerHour = newArray(7 * 24, defaultValue); + valuePerWeek = newArray(3 * 52, defaultValue); } + private static long[] newArray(int size, long value) { + long[] array = new long[size]; + fill(array, value); + return array; + } + /** * Returns the {@link AtomicLong} instance used to measure the value for * the time series. @@ -84,7 +106,7 @@ */ public synchronized void recordOneSecond() { if (resetValueEachSecond) { - valuePerSecond[seconds++] = counter.getAndSet(0); + valuePerSecond[seconds++] = counter.getAndSet(defaultValue); } else { valuePerSecond[seconds++] = counter.get(); } @@ -107,18 +129,28 @@ //----------------------------------------------------------< TimeSeries > + + @Override + public long getDefaultValue() { + return defaultValue; + } + + @Override public synchronized long[] getValuePerSecond() { return cyclicCopyFrom(valuePerSecond, seconds); } + @Override public synchronized long[] getValuePerMinute() { return cyclicCopyFrom(valuePerMinute, minutes); } + @Override public synchronized long[] getValuePerHour() { return cyclicCopyFrom(valuePerHour, hours); } + @Override public synchronized long[] getValuePerWeek() { return cyclicCopyFrom(valuePerWeek, weeks); } @@ -133,14 +165,20 @@ */ private long aggregate(long[] array) { long sum = 0; - for (int i = 0; i < array.length; i++) { - - sum += array[i]; + int count = 0; + for (long value : array) { + if (value != defaultValue) { + count++; + sum += value; - } + } - if (resetValueEachSecond) { + } + if (count == 0) { + return defaultValue; + } else if (resetValueEachSecond) { return sum; + } else { + return round((double) sum / count); } - return sum / array.length; } /** Index: jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesRecorderTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesRecorderTest.java (revision 6e582e4870279ca8c93f434e7a350c5db1d143b1) +++ jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesRecorderTest.java (revision ) @@ -23,9 +23,10 @@ public class TimeSeriesRecorderTest extends TestCase { + private TimeSeriesRecorder recorder; + public void testCounter() { - TimeSeriesRecorder recorder = new TimeSeriesRecorder( - RepositoryStatistics.Type.SESSION_READ_COUNTER); + recorder = new TimeSeriesRecorder(RepositoryStatistics.Type.SESSION_READ_COUNTER); AtomicLong counter = recorder.getCounter(); // initial values @@ -110,12 +111,103 @@ assertValues(recorder.getValuePerWeek(), 13); } + public void testCounterWithMissing() { + for (long m : new long[]{-42, 42}) { + recorder = new TimeSeriesRecorder(true, m); + AtomicLong counter = recorder.getCounter(); + + // initial values + assertValues(recorder.getValuePerSecond()); + assertValues(recorder.getValuePerMinute()); + assertValues(recorder.getValuePerHour()); + assertValues(recorder.getValuePerWeek()); + + // no changes in first second + recorder.recordOneSecond(); + assertValues(recorder.getValuePerSecond()); + assertValues(recorder.getValuePerMinute()); + assertValues(recorder.getValuePerHour()); + assertValues(recorder.getValuePerWeek()); + + // one increment in second + counter.set(0); + counter.incrementAndGet(); + recorder.recordOneSecond(); + assertValues(recorder.getValuePerSecond(), 1); + assertValues(recorder.getValuePerMinute()); + assertValues(recorder.getValuePerHour()); + assertValues(recorder.getValuePerWeek()); + + // two increments in second + counter.set(0); + counter.incrementAndGet(); + counter.incrementAndGet(); + recorder.recordOneSecond(); + assertValues(recorder.getValuePerSecond(), 2, 1); + assertValues(recorder.getValuePerMinute()); + assertValues(recorder.getValuePerHour()); + assertValues(recorder.getValuePerWeek()); + + // no changes in a second + recorder.recordOneSecond(); + assertValues(recorder.getValuePerSecond(), recorder.getDefaultValue(), 2, 1); + assertValues(recorder.getValuePerMinute()); + assertValues(recorder.getValuePerHour()); + assertValues(recorder.getValuePerWeek()); + + // ten increments in a second + counter.set(0); + counter.addAndGet(10); + recorder.recordOneSecond(); + assertValues(recorder.getValuePerSecond(), 10, recorder.getDefaultValue(), 2, 1); + assertValues(recorder.getValuePerMinute()); + assertValues(recorder.getValuePerHour()); + assertValues(recorder.getValuePerWeek()); + + // one minute + for (int i = 0; i < 60; i++) { + recorder.recordOneSecond(); + } + assertValues(recorder.getValuePerSecond()); + assertValues(recorder.getValuePerMinute(), 13); + assertValues(recorder.getValuePerHour()); + assertValues(recorder.getValuePerWeek()); + + // second minute + for (int i = 0; i < 60; i++) { + recorder.recordOneSecond(); + } + assertValues(recorder.getValuePerSecond()); + assertValues(recorder.getValuePerMinute(), recorder.getDefaultValue(), 13); + assertValues(recorder.getValuePerHour()); + assertValues(recorder.getValuePerWeek()); + + // one hour + for (int i = 0; i < 60 * 60; i++) { + recorder.recordOneSecond(); + } + assertValues(recorder.getValuePerSecond()); + assertValues(recorder.getValuePerMinute()); + assertValues(recorder.getValuePerHour(), 13); + assertValues(recorder.getValuePerWeek()); + + // one week + for (int i = 0; i < 7 * 24 * 60 * 60; i++) { + recorder.recordOneSecond(); + } + assertValues(recorder.getValuePerSecond()); + assertValues(recorder.getValuePerMinute()); + assertValues(recorder.getValuePerHour()); + assertValues(recorder.getValuePerWeek(), 13); + } + } + private void assertValues(long[] values, long... expected) { for (int i = 0; i < expected.length; i++) { assertEquals(expected[i], values[values.length - i - 1]); } for (int i = expected.length; i < values.length; i++) { - assertEquals(0, values[values.length - i - 1]); + assertEquals(recorder.getDefaultValue(), values[values.length - i - 1]); } } Index: jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesMaxTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesMaxTest.java (revision ) +++ jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesMaxTest.java (revision ) @@ -0,0 +1,170 @@ +package org.apache.jackrabbit.stats; + +import junit.framework.TestCase; + +public class TimeSeriesMaxTest extends TestCase { + private TimeSeriesMax max; + + public void testMax() { + max = new TimeSeriesMax(); + + // initial values + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // no changes in first second + max.recordOneSecond(); + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // 2 seconds + max.recordValue(42); + max.recordOneSecond(); + assertValues(max.getValuePerSecond(), 42); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // no changes in 3rd second + max.recordOneSecond(); + assertValues(max.getValuePerSecond(), 0, 42); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // 4th second + max.recordValue(99); + max.recordOneSecond(); + assertValues(max.getValuePerSecond(), 99, 0, 42); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // one minute later + for (int i = 0; i < 60; i++) { + max.recordOneSecond(); + } + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute(), 99); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // another minute later + for (int i = 0; i < 60; i++) { + max.recordOneSecond(); + } + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute(), 0, 99); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // one hour + for (int i = 0; i < 60 * 60; i++) { + max.recordOneSecond(); + } + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour(), 99); + assertValues(max.getValuePerWeek()); + + // one week + for (int i = 0; i < 7 * 24 * 60 * 60; i++) { + max.recordOneSecond(); + } + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek(), 99); + } + + public void testMaxWithMissing() { + for (long m : new long[]{-42, 42}) { + max = new TimeSeriesMax(m); + + // initial values + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // no changes in first second + max.recordOneSecond(); + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // 2 seconds + max.recordValue(42); + max.recordOneSecond(); + assertValues(max.getValuePerSecond(), 42); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // no changes in 3rd second + max.recordOneSecond(); + assertValues(max.getValuePerSecond(), max.getDefaultValue(), 42); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // 4th second + max.recordValue(99); + max.recordOneSecond(); + assertValues(max.getValuePerSecond(), 99, max.getDefaultValue(), 42); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // one minute later + for (int i = 0; i < 60; i++) { + max.recordOneSecond(); + } + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute(), 99); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // another minute later + for (int i = 0; i < 60; i++) { + max.recordOneSecond(); + } + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute(), max.getDefaultValue(), 99); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek()); + + // one hour + for (int i = 0; i < 60 * 60; i++) { + max.recordOneSecond(); + } + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour(), 99); + assertValues(max.getValuePerWeek()); + + // one week + for (int i = 0; i < 7 * 24 * 60 * 60; i++) { + max.recordOneSecond(); + } + assertValues(max.getValuePerSecond()); + assertValues(max.getValuePerMinute()); + assertValues(max.getValuePerHour()); + assertValues(max.getValuePerWeek(), 99); + } + } + + private void assertValues(long[] values, long... expected) { + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], values[values.length - i - 1]); + } + for (int i = expected.length; i < values.length; i++) { + assertEquals(max.getDefaultValue(), values[values.length - i - 1]); + } + } +} Index: jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesAverageTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesAverageTest.java (revision ) +++ jackrabbit-jcr-commons/src/test/java/org/apache/jackrabbit/stats/TimeSeriesAverageTest.java (revision ) @@ -0,0 +1,186 @@ +package org.apache.jackrabbit.stats; + +import java.util.concurrent.atomic.AtomicLong; + +import junit.framework.TestCase; + +public class TimeSeriesAverageTest extends TestCase { + private TimeSeriesAverage avg; + + public void testAverage() { + TimeSeriesRecorder values = new TimeSeriesRecorder(true); + TimeSeriesRecorder counts = new TimeSeriesRecorder(true); + avg = new TimeSeriesAverage(values, counts); + AtomicLong value = values.getCounter(); + AtomicLong count = counts.getCounter(); + + // initial values + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // no changes in first second + values.recordOneSecond(); + counts.recordOneSecond(); + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // 2 seconds + value.set(42); + count.set(2); + values.recordOneSecond(); + counts.recordOneSecond(); + assertValues(avg.getValuePerSecond(), 21); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // no changes in 3rd second + values.recordOneSecond(); + counts.recordOneSecond(); + assertValues(avg.getValuePerSecond(), 0, 21); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // one minute later + for (int i = 0; i < 60; i++) { + values.recordOneSecond(); + counts.recordOneSecond(); + } + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute(), 21); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // another minute later + for (int i = 0; i < 60; i++) { + values.recordOneSecond(); + counts.recordOneSecond(); + } + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute(), 0, 21); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // one hour + for (int i = 0; i < 60 * 60; i++) { + values.recordOneSecond(); + counts.recordOneSecond(); + } + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour(), 21); + assertValues(avg.getValuePerWeek()); + + // one week + for (int i = 0; i < 7 * 24 * 60 * 60; i++) { + values.recordOneSecond(); + counts.recordOneSecond(); + } + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek(), 21); + } + + public void testAverageWithMissing() { + for (long m : new long[]{-42, 42}) { + TimeSeriesRecorder values = new TimeSeriesRecorder(true); + TimeSeriesRecorder counts = new TimeSeriesRecorder(true); + avg = new TimeSeriesAverage(values, counts, m); + AtomicLong value = values.getCounter(); + AtomicLong count = counts.getCounter(); + + // initial values + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // no changes in first second + values.recordOneSecond(); + counts.recordOneSecond(); + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // 2 seconds + value.set(42); + count.set(2); + values.recordOneSecond(); + counts.recordOneSecond(); + assertValues(avg.getValuePerSecond(), 21); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // no changes in 3rd second + values.recordOneSecond(); + counts.recordOneSecond(); + assertValues(avg.getValuePerSecond(), avg.getDefaultValue(), 21); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // Division by 0 reported as missing + value.set(1); + count.set(0); + values.recordOneSecond(); + counts.recordOneSecond(); + + // one minute later + for (int i = 0; i < 60; i++) { + values.recordOneSecond(); + counts.recordOneSecond(); + } + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute(), 21); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // another minute later + for (int i = 0; i < 60; i++) { + values.recordOneSecond(); + counts.recordOneSecond(); + } + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute(), avg.getDefaultValue(), 21); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek()); + + // one hour + for (int i = 0; i < 60 * 60; i++) { + values.recordOneSecond(); + counts.recordOneSecond(); + } + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour(), 21); + assertValues(avg.getValuePerWeek()); + + // one week + for (int i = 0; i < 7 * 24 * 60 * 60; i++) { + values.recordOneSecond(); + counts.recordOneSecond(); + } + assertValues(avg.getValuePerSecond()); + assertValues(avg.getValuePerMinute()); + assertValues(avg.getValuePerHour()); + assertValues(avg.getValuePerWeek(), 21); + } + } + + private void assertValues(long[] values, long... expected) { + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], values[values.length - i - 1]); + } + for (int i = expected.length; i < values.length; i++) { + assertEquals(avg.getDefaultValue(), values[values.length - i - 1]); + } + } +} Index: jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesAverage.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesAverage.java (revision 6e582e4870279ca8c93f434e7a350c5db1d143b1) +++ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesAverage.java (revision ) @@ -30,31 +30,47 @@ /** Value */ private final TimeSeries counter; + private final long defaultValue; + public TimeSeriesAverage(TimeSeries value, TimeSeries counter) { + this(value, counter, 0); + } + + public TimeSeriesAverage(TimeSeries value, TimeSeries counter, long defaultValue) { this.value = value; this.counter = counter; + this.defaultValue = defaultValue; } //----------------------------------------------------------< TimeSeries > + @Override + public long getDefaultValue() { + return defaultValue; + } + + @Override public long[] getValuePerSecond() { long[] values = value.getValuePerSecond(); long[] counts = counter.getValuePerSecond(); return divide(values, counts); } + @Override public long[] getValuePerMinute() { long[] values = value.getValuePerMinute(); long[] counts = counter.getValuePerMinute(); return divide(values, counts); } + @Override public synchronized long[] getValuePerHour() { long[] values = value.getValuePerHour(); long[] counts = counter.getValuePerHour(); return divide(values, counts); } + @Override public synchronized long[] getValuePerWeek() { long[] values = value.getValuePerWeek(); long[] counts = counter.getValuePerWeek(); @@ -66,20 +82,20 @@ /** * Per-entry division of two arrays. * - * @param a array - * @param b array + * @param v array + * @param c array * @return result of division */ - private long[] divide(long[] a, long[] b) { - long[] c = new long[a.length]; - for (int i = 0; i < a.length; i++) { - if (b[i] != 0) { - c[i] = a[i] / b[i]; + private long[] divide(long[] v, long[] c) { + long[] avg = new long[v.length]; + for (int i = 0; i < v.length; i++) { + if (c[i] == 0 || v[i] == value.getDefaultValue() || c[i] == counter.getDefaultValue()) { + avg[i] = defaultValue; } else { - c[i] = 0; + avg[i] = v[i] / c[i]; } } - return c; + return avg; } } Index: jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java (revision 6e582e4870279ca8c93f434e7a350c5db1d143b1) +++ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java (revision ) @@ -19,17 +19,20 @@ package org.apache.jackrabbit.stats; +import static java.util.Arrays.fill; + import org.apache.jackrabbit.api.stats.TimeSeries; /** * Time series of the maximum value recorded in a period */ public class TimeSeriesMax implements TimeSeries { - private final MaxValue max = new MaxValue(0); - private final long[] perSecond = new long[60]; - private final long[] perMinute = new long[60]; - private final long[] perHour = new long[7 * 24]; - private final long[] perWeek = new long[3 * 52]; + private final MaxValue max; + private final long defaultValue; + private final long[] perSecond; + private final long[] perMinute; + private final long[] perHour; + private final long[] perWeek; /** Current second (index in {@link #perSecond}) */ private int seconds; @@ -43,6 +46,25 @@ /** Current week (index in {@link #perWeek}) */ private int weeks; + public TimeSeriesMax() { + this(0); + } + + public TimeSeriesMax(long defaultValue) { + this.defaultValue = defaultValue; + max = new MaxValue(defaultValue); + perSecond = newArray(60, defaultValue); + perMinute = newArray(60, defaultValue); + perHour = newArray(7 * 24, defaultValue); + perWeek = newArray(3 * 52, defaultValue); + } + + private static long[] newArray(int size, long value) { + long[] array = new long[size]; + fill(array, value); + return array; + } + public void recordValue(long value) { max.setIfMaximal(value); } @@ -53,7 +75,7 @@ * second. */ public synchronized void recordOneSecond() { - perSecond[seconds++] = max.getAndSetValue(0); + perSecond[seconds++] = max.getAndSetValue(defaultValue); if (seconds == perSecond.length) { seconds = 0; perMinute[minutes++] = max(perSecond); @@ -72,6 +94,11 @@ } @Override + public long getDefaultValue() { + return defaultValue; + } + + @Override public synchronized long[] getValuePerSecond() { return cyclicCopyFrom(perSecond, seconds); } @@ -94,11 +121,13 @@ /** * Returns the maximum of all entries in the given array. */ - private static long max(long[] array) { - long max = Long.MIN_VALUE; + private long max(long[] array) { + long max = defaultValue; for (long v : array) { - if (v > max) { + if (max == defaultValue) { max = v; + } else if (v != defaultValue) { + max = Math.max(max, v); } } return max; @@ -120,22 +149,24 @@ return reverse; } - private static class MaxValue { - private long value; + private class MaxValue { + private long max; - public MaxValue(long value) { - this.value = value; + public MaxValue(long max) { + this.max = max; } public synchronized long getAndSetValue(long value) { - long v = this.value; - this.value = value; + long v = max; + max = value; return v; } public synchronized void setIfMaximal(long value) { - if (value > this.value) { - this.value = value; + if (max == defaultValue) { + max = value; + } else if (value != defaultValue) { + max = Math.max(max, value); } } } Index: jackrabbit-api/src/main/java/org/apache/jackrabbit/api/stats/TimeSeries.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-api/src/main/java/org/apache/jackrabbit/api/stats/TimeSeries.java (revision 6e582e4870279ca8c93f434e7a350c5db1d143b1) +++ jackrabbit-api/src/main/java/org/apache/jackrabbit/api/stats/TimeSeries.java (revision ) @@ -54,4 +54,13 @@ */ long[] getValuePerWeek(); + /** + * The default value returned for a period where no value was recorded. + * It could either stand for a missing value or for a implied value. The + * exact semantics depends on the implementation. + * + * @return default value + */ + long getDefaultValue(); + }