package ctbrec.io; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class BandwidthMeter { public static final Duration MEASURE_TIMEFRAME = Duration.ofSeconds(10); private static List records = new ArrayList<>(100); private static Lock lock = new ReentrantLock(true); private static List listeners = new ArrayList<>(); private static Instant lastUpdate = Instant.EPOCH; private BandwidthMeter() { } public static void add(long bytes) { Record r = new Record(bytes); lock.lock(); try { records.add(r); } finally { lock.unlock(); } Instant oneSecondAgo = Instant.now().minus(Duration.ofSeconds(1)); if (lastUpdate.isBefore(oneSecondAgo)) { fireEvent(getThroughput(), MEASURE_TIMEFRAME); lastUpdate = Instant.now(); } } public static void setThroughput(long bytes, Duration d) { lock.lock(); try { records.clear(); records.add(new Record(bytes)); } finally { lock.unlock(); } fireEvent(bytes, d); } private static void fireEvent(long throughput, Duration timeframe) { for (Listener listener : listeners) { listener.bandwidthCalculated(throughput, timeframe); } } /** * Get the throughput over the last 10 seconds * @return throughput in bytes */ public static long getThroughput() { return getThroughput(MEASURE_TIMEFRAME); } /** * Get the throughput over the given duration * @return throughput in bytes */ public static long getThroughput(Duration d) { Instant now = Instant.now(); Instant measureStart = now.minus(d); long throughput = 0; lock.lock(); try { for (Iterator iterator = records.iterator(); iterator.hasNext();) { Record record = iterator.next(); if (record.timestamp.isBefore(measureStart)) { iterator.remove(); } else { throughput += record.bytes; } } } finally { lock.unlock(); } return throughput; } public static void addListener(Listener l) { listeners.add(l); } public static void removeListener(Listener l) { listeners.remove(l); } private static class Record { public Instant timestamp; public long bytes; public Record(long bytes) { timestamp = Instant.now(); this.bytes = bytes; } } @FunctionalInterface public static interface Listener { void bandwidthCalculated(long bytes, Duration timeframe); } }