org.hyperic.hq.stats.AbstractStatsWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.hq.stats.AbstractStatsWriter.java

Source

/**
 * NOTE: This copyright does *not* cover user programs that use Hyperic
 * program services by normal system calls through the application
 * program interfaces provided as part of the Hyperic Plug-in Development
 * Kit or the Hyperic Client Development Kit - this is merely considered
 * normal use of the program, and does *not* fall under the heading of
 *  "derived work".
 *
 *  Copyright (C) [2010], VMware, Inc.
 *  This file is part of Hyperic.
 *
 *  Hyperic is free software; you can redistribute it and/or modify
 *  it under the terms version 2 of the GNU General Public License as
 *  published by the Free Software Foundation. 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.hyperic.hq.stats;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.measurement.MeasurementConstants;
import org.hyperic.util.stats.StatCollector;
import org.hyperic.util.stats.StatUnreachableException;
import org.hyperic.util.stats.StatsObject;

public abstract class AbstractStatsWriter {
    private static final Log log = LogFactory.getLog(AbstractStatsWriter.class);

    protected static enum Retention {
        MONTHLY, YEARLY
    };

    private final AbstractStatsCollector statsCollector;
    private final Retention retentionType;
    private String currFilename;
    private FileWriter file;
    private String basedir;
    private String filePrefix;
    /** Write period is in seconds **/
    public static final int WRITE_PERIOD = 15;

    public AbstractStatsWriter(AbstractStatsCollector statsCollector, Retention retentionType, String filePrefix) {
        this.statsCollector = statsCollector;
        this.retentionType = retentionType;
        this.filePrefix = filePrefix;
    }

    public void startWriter() {
        basedir = getAndSetupBasedir();
        setFileInfo();
        printHeader();
        scheduleWithFixedDelay(new StatsWriter(), new Date(now() + (WRITE_PERIOD * 1000)), WRITE_PERIOD * 1000);
        statsCollector.setStarted(true);
        log.info("StatsCollector has started");
    }

    protected abstract ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay);

    /**
     * The expectation of the method is for the implementor to setup the directory where the stats will live
     * @return String representation of the basedir where the stats will live
     */
    protected abstract String getAndSetupBasedir();

    private void setFileInfo() {
        currFilename = getFilename(false);
        cleanupFilename(currFilename);
        File aFile = new File(currFilename);
        try {
            if (aFile.exists()) {
                String mvFilename = getFilename(true);
                aFile.renameTo(new File(mvFilename));
                gzipFile(mvFilename);
            }
            if (file != null) {
                file.close();
            }
            file = new FileWriter(currFilename, true);
        } catch (IOException e) {
            log.error(e, e);
        }
    }

    private final void cleanupFilename(String filename) {
        final File file = new File(filename);
        final File path = file.getParentFile();
        final String name = file.getName();
        final FilenameFilter filter = new FilenameFilter() {
            final String _filename = name;

            public boolean accept(File dir, String name) {
                if (dir.equals(path) && name.startsWith(_filename)) {
                    return true;
                }
                return false;
            }
        };
        final File[] files = path.listFiles(filter);
        final long oneWeekAgo = System.currentTimeMillis() - (7 * MeasurementConstants.DAY);
        for (int i = 0; i < files.length; i++) {
            if (files[i].lastModified() < oneWeekAgo) {
                files[i].delete();
            }
        }
    }

    private final void printHeader() {
        final StringBuilder buf = new StringBuilder("timestamp,");
        final String countAppend = "_COUNT";
        for (Map.Entry<String, StatCollector> entry : statsCollector.getStatKeys().entrySet()) {
            final String key = (String) entry.getKey();
            final StatCollector value = (StatCollector) entry.getValue();
            buf.append(key).append(',');
            // Only print the COUNT column if the StatCollector object doesn't
            // exist.
            // This means that the stat counts come in asynchronously rather
            // than begin calculated every interval.
            if (value == null) {
                buf.append(key).append(countAppend).append(',');
            }
        }
        try {
            file.append(buf.append("\n").toString());
            file.flush();
        } catch (IOException e) {
            log.warn(e.getMessage(), e);
        }
    }

    private final String getFilename(boolean withTimestamp) {
        final Calendar cal = Calendar.getInstance();
        final int month = 1 + cal.get(Calendar.MONTH);
        final String monthStr = (month < 10) ? "0" + month : String.valueOf(month);
        final int day = cal.get(Calendar.DAY_OF_MONTH);
        final String dayStr = (day < 10) ? "0" + day : String.valueOf(day);
        String rtn = null;
        if (retentionType == Retention.YEARLY) {
            rtn = filePrefix + "-" + monthStr + "-" + dayStr;
        } else {
            rtn = filePrefix + "-" + dayStr;
        }
        if (withTimestamp) {
            final int hour = cal.get(Calendar.HOUR_OF_DAY);
            final String hourStr = (hour < 10) ? "0" + hour : String.valueOf(hour);
            final int min = cal.get(Calendar.MINUTE);
            final String minStr = (min < 10) ? "0" + min : String.valueOf(min);
            final int sec = cal.get(Calendar.SECOND);
            final String secStr = (sec < 10) ? "0" + sec : String.valueOf(sec);
            rtn = rtn + "-" + hourStr + "." + minStr + "." + secStr;
        }
        String fs = File.separator;
        return basedir + fs + rtn + ".csv";
    }

    public static void gzipFile(final String filename) {
        new Thread() {
            public void run() {
                FileOutputStream gfile = null;
                GZIPOutputStream gstream = null;
                PrintStream pstream = null;
                BufferedReader reader = null;
                boolean succeed = false;
                try {
                    gfile = new FileOutputStream(filename + ".gz");
                    gstream = new GZIPOutputStream(gfile);
                    pstream = new PrintStream(gstream);
                    reader = new BufferedReader(new FileReader(filename));
                    String tmp;
                    while (null != (tmp = reader.readLine())) {
                        pstream.append(tmp).append("\n");
                    }
                    gstream.finish();
                    succeed = true;
                } catch (IOException e) {
                    log.warn(e.getMessage(), e);
                } finally {
                    close(gfile);
                    close(gstream);
                    close(pstream);
                    close(reader);
                    if (succeed) {
                        new File(filename).delete();
                    } else {
                        new File(filename + ".gz").delete();
                    }
                }
            }

            private void close(Closeable s) {
                if (s != null) {
                    try {
                        s.close();
                    } catch (IOException e) {
                        log.warn(e.getMessage(), e);
                    }
                }
            }
        }.start();
    }

    private class StatsWriter implements Runnable {
        public synchronized void run() {
            try {
                Map<String, List<Number>> stats = getStatsByKey();
                StringBuilder buf = getCSVBuf(stats);
                final FileWriter fw = getFileWriter();
                fw.append(buf.append("\n").toString());
                fw.flush();
            } catch (Throwable e) {
                log.warn(e.getMessage(), e);
            }
        }

        private FileWriter getFileWriter() throws IOException {
            final String filename = getFilename(false);
            if (!currFilename.equals(filename)) {
                file.close();
                gzipFile(currFilename);
                file = new FileWriter(filename, true);
                printHeader();
                currFilename = filename;
                cleanupFilename(currFilename);
            }
            return file;
        }

        private final StringBuilder getCSVBuf(Map<String, List<Number>> stats) {
            final StringBuilder rtn = new StringBuilder();
            rtn.append(System.currentTimeMillis()).append(',');
            for (Map.Entry<String, StatCollector> entry : statsCollector.getStatKeys().entrySet()) {
                String key = (String) entry.getKey();
                StatCollector stat = (StatCollector) entry.getValue();
                if (stat != null) {
                    try {
                        long value = stat.getVal();
                        rtn.append(value).append(',');
                    } catch (StatUnreachableException e) {
                        if (log.isDebugEnabled()) {
                            log.debug(e.getMessage(), e);
                        }
                        rtn.append(',');
                        continue;
                    }
                } else {
                    List<Number> list = stats.get(key);
                    long total = 0l;
                    if (list != null) {
                        for (Number val : list) {
                            total += val.longValue();
                        }
                        rtn.append(total).append(',').append(list.size()).append(",");
                    } else {
                        rtn.append(',').append(',');
                    }
                }
            }
            return rtn;
        }

        private Map<String, List<Number>> getStatsByKey() {
            final Map<String, List<Number>> rtn = new HashMap<String, List<Number>>();
            final StatsObject marker = statsCollector.generateMarker();
            List<Number> tmp;
            Object obj;
            while (marker != (obj = statsCollector.pollQueue())) {
                final StatsObject stat = (StatsObject) obj;
                final String id = stat.getId();
                // HHQ-5724 - this line should not be needed, but marker != (..) should do the trick
                // id == null is the id of the marker.
                if (id == null) {
                    break;
                }
                final Long val = stat.getVal();
                if (null == (tmp = rtn.get(id))) {
                    tmp = new ArrayList<Number>();
                    rtn.put(id, tmp);
                }
                tmp.add(new Long(val));
            }
            return rtn;
        }
    }

    protected long now() {
        return System.currentTimeMillis();
    }

}