/* * Created on Feb 1, 2013 * Created by Paul Gardner * * Copyright 2013 Azureus Software, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ package org.gudy.azureus2.core3.stats.transfer.impl; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.LineNumberReader; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.*; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.config.ParameterListener; import org.gudy.azureus2.core3.global.GlobalManager; import org.gudy.azureus2.core3.global.GlobalManagerStats; import org.gudy.azureus2.core3.stats.transfer.LongTermStats; import org.gudy.azureus2.core3.stats.transfer.LongTermStatsListener; import org.gudy.azureus2.core3.stats.transfer.StatsFactory; import org.gudy.azureus2.core3.util.AERunnable; import org.gudy.azureus2.core3.util.AsyncDispatcher; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.FileUtil; import org.gudy.azureus2.core3.util.SimpleTimer; import org.gudy.azureus2.core3.util.SystemTime; import org.gudy.azureus2.core3.util.TimerEvent; import org.gudy.azureus2.core3.util.TimerEventPerformer; import org.gudy.azureus2.core3.util.TimerEventPeriodic; import org.gudy.azureus2.plugins.PluginInterface; import org.gudy.azureus2.plugins.PluginManager; import com.aelitis.azureus.core.AzureusCore; import com.aelitis.azureus.core.AzureusCoreComponent; import com.aelitis.azureus.core.AzureusCoreLifecycleAdapter; import com.aelitis.azureus.core.dht.DHT; import com.aelitis.azureus.core.dht.transport.DHTTransportStats; import com.aelitis.azureus.core.util.CopyOnWriteList; import com.aelitis.azureus.core.util.average.Average; import com.aelitis.azureus.core.util.average.AverageFactory; import com.aelitis.azureus.plugins.dht.DHTPlugin; public class LongTermStatsImpl implements LongTermStats { private static final int VERSION = 1; private static final long MIN_IN_MILLIS = 60*1000; private static final long DAY_IN_MILLIS = 24*60*60*1000; public static final int RT_SESSION_START = 1; public static final int RT_SESSION_STATS = 2; public static final int RT_SESSION_END = 3; private AzureusCore core; private GlobalManagerStats gm_stats; private DHT[] dhts; private final int STAT_ENTRY_COUNT = 6; // totals at start of session private long st_p_sent; private long st_d_sent; private long st_p_received; private long st_d_received; private long st_dht_sent; private long st_dht_received; // session offsets at start of session private long ss_p_sent; private long ss_d_sent; private long ss_p_received; private long ss_d_received; private long ss_dht_sent; private long ss_dht_received; private long[] line_stats_prev = new long[STAT_ENTRY_COUNT]; private Average[] stat_averages = new Average[STAT_ENTRY_COUNT]; { for ( int i=0;i<STAT_ENTRY_COUNT;i++){ stat_averages[i] = AverageFactory.MovingImmediateAverage( 3 ); } } private boolean active; private boolean closing; private TimerEventPeriodic event; private PrintWriter writer; private String writer_rel_file; private DayCache day_cache; private final int MONTH_CACHE_MAX = 3; private Map<String,MonthCache> month_cache_map = new LinkedHashMap<String,MonthCache>(MONTH_CACHE_MAX,0.75f,true) { protected boolean removeEldestEntry( Map.Entry<String,MonthCache> eldest) { return size() > MONTH_CACHE_MAX; } }; private static SimpleDateFormat debug_utc_format = new SimpleDateFormat( "yyyy,MM,dd:HH:mm" ); private static SimpleDateFormat utc_date_format = new SimpleDateFormat( "yyyy,MM,dd" ); static{ debug_utc_format.setTimeZone( TimeZone.getTimeZone( "UTC" )); utc_date_format.setTimeZone( TimeZone.getTimeZone( "UTC" )); } private final File stats_dir; private long session_total; private CopyOnWriteList<Object[]> listeners = new CopyOnWriteList<Object[]>(); private AsyncDispatcher dispatcher = new AsyncDispatcher( "lts", 5000 ); private int start_of_week = -1; private int start_of_month = -1; private LongTermStatsImpl( File _stats_dir ) { stats_dir = _stats_dir; } public LongTermStatsImpl( AzureusCore _core, GlobalManagerStats _gm_stats ) { core = _core; gm_stats = _gm_stats; stats_dir = FileUtil.getUserFile( "stats" ); COConfigurationManager.addParameterListener( "long.term.stats.enable", new ParameterListener() { public void parameterChanged( String name) { boolean enabled = COConfigurationManager.getBooleanParameter( name ); synchronized( LongTermStatsImpl.this ){ if ( enabled ){ if ( !active ){ sessionStart(); } }else{ if ( active ){ sessionEnd(); } } } } }); _core.addLifecycleListener( new AzureusCoreLifecycleAdapter() { public void componentCreated( AzureusCore core, AzureusCoreComponent component ) { if ( component instanceof GlobalManager ){ synchronized( LongTermStatsImpl.this ){ sessionStart(); } } } public void stopped( AzureusCore core ) { synchronized( LongTermStatsImpl.this ){ closing = true; if ( active ){ sessionEnd(); } } } }); } public boolean isEnabled() { synchronized( this ){ return( active ); } } private DHT[] getDHTs() { if ( dhts == null ){ try{ PluginManager pm = core.getPluginManager(); if ( pm.isInitialized()){ PluginInterface dht_pi = pm.getPluginInterfaceByClass( DHTPlugin.class ); if ( dht_pi == null ){ dhts = new DHT[0]; }else{ DHTPlugin plugin = (DHTPlugin)dht_pi.getPlugin(); if ( !plugin.isInitialising()){ if ( plugin.isEnabled()){ dhts = ((DHTPlugin)dht_pi.getPlugin()).getDHTs(); }else{ dhts = new DHT[0]; } } } } }catch( Throwable e ){ dhts = new DHT[0]; } } return( dhts ); } private void sessionStart() { OverallStatsImpl stats = (OverallStatsImpl)StatsFactory.getStats(); synchronized( this ){ if ( closing ){ return; } boolean enabled = COConfigurationManager.getBooleanParameter( "long.term.stats.enable" ); if ( active || !enabled ){ return; } active = true; long[] snap = stats.getLastSnapshot(); ss_d_received = gm_stats.getTotalDataBytesReceived(); ss_p_received = gm_stats.getTotalProtocolBytesReceived(); ss_d_sent = gm_stats.getTotalDataBytesSent(); ss_p_sent = gm_stats.getTotalProtocolBytesSent(); ss_dht_sent = 0; ss_dht_received = 0; if ( core.isStarted()){ DHT[] dhts = getDHTs(); if ( dhts != null ){ for ( DHT dht: dhts ){ DHTTransportStats dht_stats = dht.getTransport().getStats(); ss_dht_sent += dht_stats.getBytesSent(); ss_dht_received += dht_stats.getBytesReceived(); } } } st_p_sent = snap[0] + ( ss_p_sent - snap[6]); st_d_sent = snap[1] + ( ss_d_sent - snap[7]); st_p_received = snap[2] + ( ss_p_received - snap[8]); st_d_received = snap[3] + ( ss_d_received - snap[9]); st_dht_sent = snap[4] + ( ss_dht_sent - snap[10]); st_dht_received = snap[5] + ( ss_dht_received - snap[11]); write( RT_SESSION_START, new long[]{ st_p_sent, st_d_sent, st_p_received, st_d_received, st_dht_sent, st_dht_received }); if ( event == null ){ // should always be null but hey ho event = SimpleTimer.addPeriodicEvent( "LongTermStats", MIN_IN_MILLIS, new TimerEventPerformer() { public void perform(TimerEvent event) { updateStats(); } }); } } } private void sessionEnd() { synchronized( this ){ if ( !active ){ return; } updateStats( RT_SESSION_END ); active = false; if ( event != null ){ event.cancel(); event = null; } } } private void updateStats() { updateStats( RT_SESSION_STATS ); } private void updateStats( int record_type ) { long current_d_received = gm_stats.getTotalDataBytesReceived(); long current_p_received = gm_stats.getTotalProtocolBytesReceived(); long current_d_sent = gm_stats.getTotalDataBytesSent(); long current_p_sent = gm_stats.getTotalProtocolBytesSent(); long current_dht_sent = 0; long current_dht_received = 0; DHT[] dhts = getDHTs(); if ( dhts != null ){ for ( DHT dht: dhts ){ DHTTransportStats dht_stats = dht.getTransport().getStats(); current_dht_sent += dht_stats.getBytesSent(); current_dht_received += dht_stats.getBytesReceived(); } } write( record_type, new long[]{ ( current_p_sent - ss_p_sent ), ( current_d_sent - ss_d_sent ), ( current_p_received - ss_p_received ), ( current_d_received - ss_d_received ), ( current_dht_sent - ss_dht_sent ), ( current_dht_received - ss_dht_received )}); } private void write( int record_type, long[] line_stats ) { synchronized( this ){ try{ final long now = SystemTime.getCurrentTime(); final long now_mins = now/(1000*60); String[] bits = utc_date_format.format( new Date( now )).split( "," ); String year = bits[0]; String month = bits[1]; String day = bits[2]; String current_rel_file = year + File.separator + month + File.separator + day + ".dat"; String line; String stats_str = ""; if ( record_type == RT_SESSION_START ){ // absolute values for ( int i=0;i<line_stats.length;i++ ){ stats_str += "," + line_stats[i]; line_stats_prev[i] = 0; } day_cache = null; }else{ // relative values long[] diffs = new long[STAT_ENTRY_COUNT]; for ( int i=0;i<line_stats.length;i++ ){ long diff = line_stats[i] - line_stats_prev[i]; session_total += diff; diffs[i] = diff; stats_str += "," + diff; line_stats_prev[i] = line_stats[i]; stat_averages[i].update( diff ); } if ( day_cache != null ){ if ( day_cache.isForDay( year, month, day )){ day_cache.addRecord( now_mins, diffs ); } } } if ( record_type != RT_SESSION_STATS ){ line = (record_type==RT_SESSION_START?"s,":"e,") + VERSION + "," + now_mins + stats_str; }else{ line = stats_str.substring(1); } if ( writer == null || !writer_rel_file.equals( current_rel_file )){ // first open of a file or file switch if ( writer != null ){ // file switch if ( record_type != RT_SESSION_START ){ writer.println( line ); } writer.close(); if ( writer.checkError()){ writer = null; throw( new IOException( "Write faled" )); } writer = null; } // no point in opening a new file just to record the session-end if ( record_type != RT_SESSION_END ){ File file = new File( stats_dir, current_rel_file ); file.getParentFile().mkdirs(); writer = new PrintWriter( new FileWriter( file, true )); writer_rel_file = current_rel_file; if ( record_type == RT_SESSION_START ){ writer.println( line ); }else{ // first entry in a new file, files always start with a session-start so they // can be processed in isolation so reset the session data and start a new one st_p_sent += line_stats[0]; st_d_sent += line_stats[1]; st_p_received += line_stats[2]; st_d_received += line_stats[3]; st_dht_sent += line_stats[4]; st_dht_received += line_stats[5]; ss_p_sent += line_stats[0]; ss_d_sent += line_stats[1]; ss_p_received += line_stats[2]; ss_d_received += line_stats[3]; ss_dht_sent += line_stats[4]; ss_dht_received += line_stats[5]; stats_str = ""; long[] st_stats = new long[]{ st_p_sent,st_d_sent, st_p_received,st_d_received, st_dht_sent, st_dht_received }; for ( int i=0;i<st_stats.length; i++ ){ stats_str += "," + st_stats[i]; line_stats_prev[i] = 0; } line = "s," + VERSION + "," + now_mins + stats_str; writer.println( line ); } } }else{ writer.println( line ); } }catch( Throwable e ){ Debug.out( "Failed to write long term stats", e ); }finally{ if ( writer != null ){ if ( record_type == RT_SESSION_END ){ writer.close(); } if ( writer.checkError()){ Debug.out( "Failed to write long term stats" ); writer.close(); writer = null; }else{ if ( record_type == RT_SESSION_END ){ writer = null; } } } } } if ( record_type != RT_SESSION_END ){ final List<LongTermStatsListener> to_fire = new ArrayList<LongTermStatsListener>(); for ( Object[] entry: listeners ){ long diff = session_total - (Long)entry[2]; if ( diff >= (Long)entry[1]){ entry[2] = session_total; to_fire.add((LongTermStatsListener)entry[0]); } } if ( to_fire.size() > 0 ){ dispatcher.dispatch( new AERunnable() { @Override public void runSupport() { for ( LongTermStatsListener l: to_fire ){ try{ l.updated( LongTermStatsImpl.this ); }catch( Throwable e ){ Debug.out( e ); } } } }); } } } private static String getString( long[] stats ) { String str = ""; for ( long s: stats ){ str += (str.length()==0?"":", ") + s; } return( str ); } private MonthCache getMonthCache( String year, String month ) { String key = year + "_" + month; MonthCache cache = month_cache_map.get( key ); if ( cache == null ){ cache = new MonthCache( year, month ); month_cache_map.put( key, cache ); } return( cache ); } public long[] getTotalUsageInPeriod( Date start_date, Date end_date ) { return( getTotalUsageInPeriod( start_date, end_date, null )); } public long[] getTotalUsageInPeriod( Date start_date, Date end_date, RecordAccepter accepter ) { boolean enable_caching = accepter == null; synchronized( this ){ long[] result = new long[STAT_ENTRY_COUNT]; long start_millis = start_date.getTime(); long end_millis = end_date.getTime(); long now = SystemTime.getCurrentTime(); long now_day = (now/DAY_IN_MILLIS)*DAY_IN_MILLIS; if ( end_millis > now ){ end_millis = now; } long start_day = (start_millis/DAY_IN_MILLIS)*DAY_IN_MILLIS; long end_day = (end_millis/DAY_IN_MILLIS)*DAY_IN_MILLIS; if ( start_day > end_day ){ return( result ); } long start_offset = start_millis - start_day; start_offset = start_offset/MIN_IN_MILLIS; boolean offset_cachable = start_offset % 60 == 0; System.out.println( "start=" + debug_utc_format.format( start_date ) + ", end=" + debug_utc_format.format( end_date ) + ", offset=" + start_offset); MonthCache month_cache = null; for ( long this_day=start_day;this_day<=end_day;this_day+=DAY_IN_MILLIS ){ String[] bits = utc_date_format.format( new Date( this_day )).split( "," ); String year_str = bits[0]; String month_str = bits[1]; String day_str = bits[2]; int year = Integer.parseInt( year_str ); int month = Integer.parseInt( month_str ); int day = Integer.parseInt( day_str ); long cache_offset = this_day == start_day?start_offset:0; boolean can_cache; if ( enable_caching ){ if ( month_cache == null || !month_cache.isForMonth( year_str, month_str )){ if ( month_cache != null && month_cache.isDirty()){ month_cache.save(); } month_cache = getMonthCache( year_str, month_str ); } can_cache = this_day != now_day && ( this_day > start_day || ( this_day == start_day && offset_cachable )) && this_day < end_day; if ( can_cache ){ long[] cached_totals = month_cache.getTotals( day, cache_offset ); if ( cached_totals != null ){ for ( int i=0;i<cached_totals.length;i++){ result[i] += cached_totals[i]; } continue; } }else{ if ( this_day == now_day ){ if ( day_cache != null ){ if ( day_cache.isForDay( year_str, month_str, day_str )){ long[] cached_totals = day_cache.getTotals( cache_offset ); if ( cached_totals != null ){ for ( int i=0;i<cached_totals.length;i++){ result[i] += cached_totals[i]; } continue; } }else{ day_cache = null; } } } } }else{ can_cache = false; } String current_rel_file = bits[0] + File.separator + bits[1] + File.separator + bits[2] + ".dat"; File stats_file = new File( stats_dir, current_rel_file ); if ( !stats_file.exists()){ if ( can_cache ){ month_cache.setTotals( day, cache_offset, new long[0] ); } }else{ LineNumberReader lnr = null; try{ System.out.println( "Reading " + stats_file ); lnr = new LineNumberReader( new FileReader( stats_file )); long file_start_time = 0; long[] file_totals = null; long[] file_result_totals = new long[STAT_ENTRY_COUNT]; long[] session_start_stats = null; long session_start_time = 0; long session_time = 0; while( true ){ String line = lnr.readLine(); if ( line == null ){ break; } //System.out.println( line ); String[] fields = line.split( "," ); if ( fields.length < 6 ){ continue; } String first_field = fields[0]; if ( first_field.equals("s")){ session_start_time = Long.parseLong( fields[2] )*MIN_IN_MILLIS; if ( file_totals == null ){ file_totals = new long[STAT_ENTRY_COUNT]; file_start_time = session_start_time; } session_time = session_start_time; session_start_stats = new long[STAT_ENTRY_COUNT]; for ( int i=3;i<9;i++){ session_start_stats[i-3] = Long.parseLong( fields[i] ); } }else if ( session_start_time > 0 ){ session_time += MIN_IN_MILLIS; int field_offset = 0; if ( first_field.equals( "e" )){ field_offset = 3; } long[] line_stats = new long[STAT_ENTRY_COUNT]; for ( int i=0;i<6;i++){ line_stats[i] = Long.parseLong( fields[i+field_offset] ); file_totals[i] += line_stats[i]; } if ( session_time >= start_millis && session_time <= end_millis ){ if ( accepter == null || accepter.acceptRecord( session_time )){ for ( int i=0;i<6;i++){ result[i] += line_stats[i]; file_result_totals[i] += line_stats[i]; } } } //System.out.println( getString( line_stats )); } } if ( file_totals == null ){ file_totals = new long[0]; } System.out.println( "File total: start=" + debug_utc_format.format(file_start_time) + ", end=" + debug_utc_format.format(session_time) + " - " + getString( file_totals )); if ( can_cache ){ month_cache.setTotals( day, cache_offset, file_result_totals ); if ( cache_offset != 0 ){ month_cache.setTotals( day, 0, file_totals ); } }else{ if ( enable_caching ){ if ( this_day == now_day ){ if ( day_cache == null ){ System.out.println( "Creating day cache" ); day_cache = new DayCache( year_str, month_str, day_str ); } day_cache.setTotals( cache_offset, file_result_totals ); if ( cache_offset != 0 ){ day_cache.setTotals( 0, file_totals ); } } } } }catch( Throwable e ){ Debug.out( e ); }finally{ if ( lnr != null ){ try{ lnr.close(); }catch( Throwable e ){ } } } } } if ( enable_caching ){ if ( month_cache != null && month_cache.isDirty()){ month_cache.save(); } } System.out.println( " -> " + getString( result )); return( result ); } } public long[] getTotalUsageInPeriod( int period_type ) { return( getTotalUsageInPeriod( period_type, null )); } public long[] getTotalUsageInPeriod( int period_type, RecordAccepter accepter ) { if ( start_of_week == -1 ){ COConfigurationManager.addAndFireParameterListeners( new String[]{ "long.term.stats.weekstart", "long.term.stats.monthstart" }, new ParameterListener() { public void parameterChanged( String name ) { start_of_week = COConfigurationManager.getIntParameter( "long.term.stats.weekstart" ); start_of_month = COConfigurationManager.getIntParameter( "long.term.stats.monthstart" ); } }); } Calendar calendar = new GregorianCalendar(); calendar.setTimeInMillis( SystemTime.getCurrentTime()); calendar.set( Calendar.MILLISECOND, 0 ); calendar.set( Calendar.MINUTE, 0 ); calendar.set( Calendar.HOUR_OF_DAY, 0 ); long top_time = calendar.getTimeInMillis() + DAY_IN_MILLIS - 1; if ( period_type == PT_CURRENT_DAY ){ }else if ( period_type == PT_CURRENT_WEEK ){ // sun = 1, mon = 2 etc int day_of_week = calendar.get( Calendar.DAY_OF_WEEK ); if ( day_of_week == start_of_week ){ }else if ( day_of_week > start_of_week ){ calendar.add( Calendar.DAY_OF_WEEK, - ( day_of_week - start_of_week )); }else{ calendar.add( Calendar.DAY_OF_WEEK, - ( 7 - ( start_of_week - day_of_week ))); } }else{ if ( start_of_month == 1 ){ calendar.set( Calendar.DAY_OF_MONTH, 1 ); }else{ int day_of_month = calendar.get( Calendar.DAY_OF_MONTH ); if ( day_of_month == start_of_month ){ }else if ( day_of_month > start_of_month ){ calendar.set( Calendar.DAY_OF_MONTH, start_of_month ); }else{ calendar.add( Calendar.MONTH, -1 ); calendar.set( Calendar.DAY_OF_MONTH, start_of_month ); } } } long bottom_time = calendar.getTimeInMillis(); return( getTotalUsageInPeriod( new Date( bottom_time ), new Date( top_time ), accepter )); } public long[] getCurrentRateBytesPerSecond() { long[] result = new long[STAT_ENTRY_COUNT]; for ( int i=0;i<STAT_ENTRY_COUNT;i++){ result[i] = (long)( stat_averages[i].getAverage()/60 ); } return( result ); } public void addListener( long min_delta_bytes, final LongTermStatsListener listener ) { listeners.add( new Object[]{ listener, min_delta_bytes, session_total }); dispatcher.dispatch( new AERunnable() { public void runSupport() { listener.updated( LongTermStatsImpl.this ); } }); } public void removeListener( LongTermStatsListener listener ) { for ( Object[] entry: listeners ){ if ( entry[0] == listener ){ listeners.remove( entry ); break; } } } private class DayCache { private String year; private String month; private String day; private Map<Long,long[]> contents = new HashMap<Long, long[]>(); private DayCache( String _year, String _month, String _day ) { year = _year; month = _month; day = _day; } private boolean isForDay( String _year, String _month, String _day ) { return( year.equals( _year ) && month.equals( _month ) && day.equals( _day )); } private void addRecord( long offset, long[] stats ) { for ( Map.Entry<Long,long[]> entry: contents.entrySet()){ if ( offset >= entry.getKey()){ long[] old = entry.getValue(); for ( int i=0;i<old.length;i++){ old[i] += stats[i]; } } } } private long[] getTotals( long offset ) { return( contents.get( offset )); } private void setTotals( long offset, long[] value ) { contents.put( offset, value ); } } private class MonthCache { private String year; private String month; private boolean dirty; private Map<String,List<Long>> contents; private MonthCache( String _year, String _month ) { year = _year; month = _month; } private File getCacheFile() { return( new File( stats_dir, year + File.separator + month + File.separator + "cache.dat" )); } private boolean isForMonth( String _year, String _month ) { return( year.equals( _year ) && month.equals( _month )); } private Map<String,List<Long>> getContents() { if ( contents == null ){ File file = getCacheFile(); if ( file.exists()){ System.out.println( "Reading cache: " + file ); contents = FileUtil.readResilientFile( file ); }else{ contents = new HashMap<String, List<Long>>(); } } return( contents ); } private long[] getTotals( int day ) { List<Long> records = getContents().get( String.valueOf( day )); if ( records != null ){ long[] result = new long[STAT_ENTRY_COUNT]; if ( records.size() == STAT_ENTRY_COUNT ){ for ( int i=0;i<STAT_ENTRY_COUNT;i++){ result[i] = (Long)records.get(i); } } return( result ); } return( null ); } private long[] getTotals( int day, long start_offset ) { if ( start_offset == 0 ){ return( getTotals( day )); }else{ List<Long> records = getContents().get( day + "." + start_offset ); if ( records != null ){ long[] result = new long[STAT_ENTRY_COUNT]; if ( records.size() == STAT_ENTRY_COUNT ){ for ( int i=0;i<STAT_ENTRY_COUNT;i++){ result[i] = (Long)records.get(i); } } return( result ); } return( null ); } } private void setTotals( int day, long[] totals ) { List<Long> records = new ArrayList<Long>(); for ( Long l: totals ){ records.add( l ); } getContents().put( String.valueOf( day ), records ); dirty = true; } private void setTotals( int day, long start_offset, long[] totals ) { if ( start_offset == 0 ){ setTotals( day, totals ); }else{ List<Long> records = new ArrayList<Long>(); for ( Long l: totals ){ records.add( l ); } getContents().put( day + "." + start_offset, records ); dirty = true; } } private boolean isDirty() { return( dirty ); } private void save() { File file = getCacheFile(); file.getParentFile().mkdirs(); System.out.println( "Writing cache: " + file ); FileUtil.writeResilientFile( file, contents ); dirty = false; } } public static void main( String[] args ) { try{ LongTermStatsImpl impl = new LongTermStatsImpl( new File( "C:\\Test\\plus2\\stats" )); SimpleDateFormat local_format = new SimpleDateFormat( "yyyy,MM,dd" ); Date start_date = local_format.parse( "2013,07,10" ); Date end_date = local_format.parse( "2013,07,16" ); long[] usage = impl.getTotalUsageInPeriod( start_date, end_date, new RecordAccepter() { public boolean acceptRecord( long timestamp) { System.out.println( new Date( timestamp )); return( false ); } }); System.out.println( getString( usage )); //System.out.println( getString(impl.getTotalUsageInPeriod( PT_CURRENT_DAY ))); //System.out.println( getString(impl.getTotalUsageInPeriod( PT_CURRENT_WEEK ))); //System.out.println( getString(impl.getTotalUsageInPeriod( PT_CURRENT_MONTH ))); }catch( Throwable e ){ e.printStackTrace(); } } }