package net.i2p.stat; import java.io.IOException; import java.io.OutputStream; import static java.util.Arrays.*; import java.util.Properties; import net.i2p.data.DataHelper; /** coordinate a moving rate over various periods */ public class RateStat { /** unique name of the statistic */ private final String _statName; /** grouping under which the stat is kept */ private final String _groupName; /** describe the stat */ private final String _description; /** actual rate objects for this statistic */ protected final Rate[] _rates; /** component we tell about events as they occur */ private StatLog _statLog; public RateStat(String name, String description, String group, long periods[]) { _statName = name; _description = description; _groupName = group; if (periods.length == 0) throw new IllegalArgumentException(); long [] periodsCopy = new long[periods.length]; System.arraycopy(periods, 0, periodsCopy, 0, periods.length); sort(periodsCopy); _rates = new Rate[periodsCopy.length]; for (int i = 0; i < periodsCopy.length; i++) { Rate rate = new Rate(periodsCopy[i]); rate.setRateStat(this); _rates[i] = rate; } } public void setStatLog(StatLog sl) { _statLog = sl; } /** * update all of the rates for the various periods with the given value. */ public void addData(long value, long eventDuration) { if (_statLog != null) _statLog.addData(_groupName, _statName, value, eventDuration); for (Rate r: _rates) r.addData(value, eventDuration); } /** * Update all of the rates for the various periods with the given value. * Zero duration. * @since 0.8.10 */ public void addData(long value) { if (_statLog != null) _statLog.addData(_groupName, _statName, value, 0); for (Rate r: _rates) r.addData(value); } /** coalesce all the stats */ public void coalesceStats() { for (Rate r: _rates) r.coalesce(); } public String getName() { return _statName; } public String getGroupName() { return _groupName; } public String getDescription() { return _description; } public long[] getPeriods() { long [] rv = new long[_rates.length]; for (int i = 0; i < _rates.length; i++) rv[i] = _rates[i].getPeriod(); return rv; } public double getLifetimeAverageValue() { return _rates[0].getLifetimeAverageValue(); } public long getLifetimeEventCount() { return _rates[0].getLifetimeEventCount(); } /** * Returns rate with requested period if it exists, * otherwise null * @param period ms * @return the Rate */ public Rate getRate(long period) { for (Rate r : _rates) { if (r.getPeriod() == period) return r; } return null; } /** * Adds a new rate with the requested period, provided that * a rate with that period does not already exist. * @param period ms * @since 0.8.8 */ @Deprecated public void addRate(long period) { throw new UnsupportedOperationException(); } /** * If a rate with the provided period exists, remove it. * @param period ms * @since 0.8.8 */ @Deprecated public void removeRate(long period) { throw new UnsupportedOperationException(); } /** * Tests if a rate with the provided period exists within this RateStat. * @param period ms * @return true if exists * @since 0.8.8 */ public boolean containsRate(long period) { return getRate(period) != null; } @Override public int hashCode() { return _statName.hashCode(); } private final static String NL = System.getProperty("line.separator"); @Override public String toString() { StringBuilder buf = new StringBuilder(4096); buf.append(getGroupName()).append('.').append(getName()).append(": ").append(getDescription()).append('\n'); long periods[] = getPeriods(); sort(periods); for (int i = 0; i < periods.length; i++) { buf.append('\t').append(periods[i]).append(':'); Rate curRate = getRate(periods[i]); buf.append(curRate.toString()); buf.append(NL); } return buf.toString(); } @Override public boolean equals(Object obj) { if ((obj == null) || !(obj instanceof RateStat)) return false; if (obj == this) return true; RateStat rs = (RateStat) obj; if (nameGroupDescEquals(rs)) return deepEquals(this._rates, rs._rates); return false; } boolean nameGroupDescEquals(RateStat rs) { return DataHelper.eq(getGroupName(), rs.getGroupName()) && DataHelper.eq(getDescription(), rs.getDescription()) && DataHelper.eq(getName(), rs.getName()); } public void store(OutputStream out, String prefix) throws IOException { StringBuilder buf = new StringBuilder(1024); buf.append(NL); buf.append("################################################################################").append(NL); buf.append("# Rate: ").append(_groupName).append(": ").append(_statName).append(NL); buf.append("# ").append(_description).append(NL); buf.append("# ").append(NL).append(NL); out.write(buf.toString().getBytes("UTF-8")); buf.setLength(0); for (Rate r: _rates){ buf.append("#######").append(NL); buf.append("# Period : ").append(DataHelper.formatDuration(r.getPeriod())).append(" for rate ") .append(_groupName).append(" - ").append(_statName).append(NL); buf.append(NL); String curPrefix = prefix + "." + DataHelper.formatDuration(r.getPeriod()); r.store(curPrefix, buf); out.write(buf.toString().getBytes("UTF-8")); buf.setLength(0); } } /** * Load this rate stat from the properties, populating all of the rates contained * underneath it. The comes from the given prefix (e.g. if we are given the prefix * "profile.dbIntroduction", a series of rates may be found underneath * "profile.dbIntroduction.60s", "profile.dbIntroduction.60m", and "profile.dbIntroduction.24h"). * This RateStat must already be created, with the specified rate entries constructued - this * merely loads them with data. * * @param prefix prefix to the property entries (should NOT end with a period) * @param treatAsCurrent if true, we'll treat the loaded data as if no time has * elapsed since it was written out, but if it is false, we'll * treat the data with as much freshness (or staleness) as appropriate. * @throws IllegalArgumentException if the data was formatted incorrectly */ public void load(Properties props, String prefix, boolean treatAsCurrent) throws IllegalArgumentException { for (Rate r : _rates) { long period = r.getPeriod(); String curPrefix = prefix + "." + DataHelper.formatDuration(period); r.load(props, curPrefix, treatAsCurrent); } } /********* public static void main(String args[]) { RateStat rs = new RateStat("moo", "moo moo moo", "cow trueisms", new long[] { 60 * 1000, 60 * 60 * 1000, 24 * 60 * 60 * 1000}); for (int i = 0; i < 500; i++) { try { Thread.sleep(20); } catch (InterruptedException ie) { // nop } rs.addData(i * 100, 20); } rs.coalesceStats(); java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(2048); try { rs.store(baos, "rateStat.test"); byte data[] = baos.toByteArray(); _log.error("Stored rateStat: size = " + data.length + "\n" + new String(data)); Properties props = new Properties(); props.load(new java.io.ByteArrayInputStream(data)); //_log.error("Properties loaded: \n" + props); RateStat loadedRs = new RateStat("moo", "moo moo moo", "cow trueisms", new long[] { 60 * 1000, 60 * 60 * 1000, 24 * 60 * 60 * 1000}); loadedRs.load(props, "rateStat.test", true); _log.error("Comparison after store/load: " + rs.equals(loadedRs)); } catch (Throwable t) { _log.error("b0rk", t); } try { Thread.sleep(5000); } catch (InterruptedException ie) { // nop } } *********/ }