package com.limegroup.gnutella; import com.limegroup.gnutella.settings.ApplicationSettings; /** * Maintains various session statistics, like uptime. Implements the Singleton * pattern. Statistics are initialized the when the class is loaded; call * Statistics.instance() to guarantee initialization. */ public class Statistics { private static Statistics _instance=new Statistics(); /** "PROTECTED" FOR TESTING PURPOSES ONLY! */ protected Statistics() {} /** Returns the single Statistics instance. */ public static Statistics instance() { return _instance; } ///////////////////////////////////////////////////////////////////// /** The number of seconds in a day. */ private static final int SECONDS_PER_DAY=24*60*60; /** Controls how much past is remembered in calculateFractionalUptime. * Default: 7 days, which doesn't quite mean what you might think * see calculateFractionalUptime. */ private static final int WINDOW_MILLISECONDS=7*SECONDS_PER_DAY*1000; /** The time this was initialized. */ private long startTime=now(); /** * Returns the amount of time this has been running. * @return the session uptime in milliseconds */ public long getUptime() { //TODO: clocks can go backwards from daylight savings, resulting in //negative values. return now() - startTime; } /** * Calculates the average number of seconds this host runs per day, i.e., * calculateFractionRunning*24*60*60. * @return uptime in seconds/day. * @see calculateFractionalUptime */ public int calculateDailyUptime() { return (int)(calculateFractionalUptime()*(float)SECONDS_PER_DAY); } /** * Calculates the fraction of time this is running, a unitless quantity * between zero and 1. Implemented using an exponential moving average * (EMA) that discounts the past. Does not update the FRACTION_RUNNING * property; that should only be done once, on shutdown * @see calculateDailyUptime */ public float calculateFractionalUptime() { //Let // P = the last value returned by calculateFractionRunning stored // in the SettingsMangager // W = the window size in seconds. (See note below.) // t = the uptime for this session. It is assumed that // t<W; otherwise set t=W. // T = the elapsed time since the end of the previous session, i.e., // since P' was updated. Note that t<T. It is assumed that // T<W; otherwise set T=W. // //The new fraction P' of the time this is running can be calculated as //a weighted average of the current session (t/T) and the past (P): // P' = (T/W)*t/T + (1-T/W)*P // = t/W + (W-T)/W*P // //W's name is misleading, because more than W seconds worth of history //are factored into the calculation. More specifically, a session i //days ago contributes 1/W * ((W-1)/W)^i part of the average. The //default value of W (7 days) means, for example, that the past 9 days //account for 75% of the calculation. final float W=WINDOW_MILLISECONDS; float T=Math.min(W, now() - ApplicationSettings.LAST_SHUTDOWN_TIME.getValue()); float t=Math.min(W, getUptime()); float P=ApplicationSettings.FRACTIONAL_UPTIME.getValue(); //Occasionally clocks can go backwards, e.g., if user adjusts them or //from daylight savings time. In this case, ignore the current session //and just return P. if (t<0 || T<0 || t>T) return P; return t/W + (W-T)/W*P; } /** * Notifies this that LimeWire is shutting down, updating permanent * statistics in limewire.props if necessary. */ public void shutdown() { //Order matters, as calculateFractionalUptime() depends on the //LAST_SHUTDOWN_TIME property. ApplicationSettings.FRACTIONAL_UPTIME.setValue(calculateFractionalUptime()); ApplicationSettings.LAST_SHUTDOWN_TIME.setValue(now()); int sessions = ApplicationSettings.SESSIONS.getValue(); ApplicationSettings.SESSIONS.setValue( sessions + 1 ); } /** The current system time, in milliseconds. Exists as a hook for testing * purposes only. */ protected long now() { return System.currentTimeMillis(); } }