com.googlecode.psiprobe.model.stats.StatsCollection.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.psiprobe.model.stats.StatsCollection.java

Source

/*
 * Licensed under the GPL License.  You may not use this file except in
 * compliance with the License.  You may obtain a copy of the License at
 *
 *     http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */
package com.googlecode.psiprobe.model.stats;

import com.googlecode.psiprobe.tools.UpdateCommitLock;
import com.thoughtworks.xstream.XStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jfree.data.xy.XYDataItem;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.context.WebApplicationContext;

/**
 * 
 * @author Vlad Ilyushchenko
 * @author Andy Shapoval
 * @author Mark Lewis
 */
public class StatsCollection implements InitializingBean, DisposableBean, ApplicationContextAware {

    private Log logger = LogFactory.getLog(this.getClass());

    private Map statsData = new TreeMap();
    private String swapFileName;
    private String storagePath = null;
    private File contextTempDir;
    private int maxFiles = 2;
    private final UpdateCommitLock lock = new UpdateCommitLock();

    public String getSwapFileName() {
        return swapFileName;
    }

    public void setSwapFileName(String swapFileName) {
        this.swapFileName = swapFileName;
    }

    public String getStoragePath() {
        return storagePath;
    }

    public void setStoragePath(String storagePath) {
        this.storagePath = storagePath;
    }

    public boolean isCollected(String statsName) {
        return statsData.get(statsName) != null;
    }

    public int getMaxFiles() {
        return maxFiles;
    }

    public void setMaxFiles(int maxFiles) {
        this.maxFiles = maxFiles > 0 ? maxFiles : 2;
    }

    public synchronized List newStats(String name, int maxElements) {
        List stats = Collections.synchronizedList(new ArrayList(maxElements));
        statsData.put(name, stats);
        return stats;
    }

    public synchronized void resetStats(String name) {
        List stats = getStats(name);
        if (stats != null) {
            stats.clear();
        }
    }

    public synchronized List getStats(String name) {
        return (List) statsData.get(name);
    }

    public long getLastValueForStat(String statName) {
        long statValue = 0;

        List stats = getStats(statName);
        if (stats != null && !stats.isEmpty()) {
            XYDataItem xy = (XYDataItem) stats.get(stats.size() - 1);
            if (xy != null && xy.getY() != null) {
                statValue = xy.getY().longValue();
            }
        }

        return statValue;
    }

    /**
     * Returns series if stat name starts with the prefix.
     * @param statNamePrefix
     * @return a Map of matching stats. Map keys are stat names and map values are corresponding series.
     */
    public synchronized Map getStatsByPrefix(String statNamePrefix) {
        Map map = new HashMap();
        for (Iterator i = statsData.entrySet().iterator(); i.hasNext();) {
            Map.Entry en = (Map.Entry) i.next();
            if (((String) en.getKey()).startsWith(statNamePrefix)) {
                map.put(en.getKey(), en.getValue());
            }
        }
        return map;
    }

    private File makeFile() {
        return storagePath == null ? new File(contextTempDir, swapFileName) : new File(storagePath, swapFileName);
    }

    private void shiftFiles(int index) {
        if (index >= maxFiles - 1) {
            new File(makeFile().getAbsolutePath() + "." + index).delete();
        } else {
            shiftFiles(index + 1);
            File srcFile = index == 0 ? makeFile() : new File(makeFile().getAbsolutePath() + "." + index);
            File destFile = new File(makeFile().getAbsolutePath() + "." + (index + 1));
            srcFile.renameTo(destFile);
        }
    }

    /**
     * Writes stats data to file on disk.
     *
     * @throws IOException
     * @throws InterruptedException
     */
    public synchronized void serialize() throws IOException, InterruptedException {
        lock.lockForCommit();
        long t = System.currentTimeMillis();
        try {
            shiftFiles(0);
            OutputStream os = new FileOutputStream(makeFile());
            try {
                new XStream().toXML(statsData, os);
            } finally {
                os.close();
            }
        } catch (Exception e) {
            logger.error("Could not write stats data to " + makeFile().getAbsolutePath(), e);
        } finally {
            lock.releaseCommitLock();
            logger.debug("stats serialized in " + (System.currentTimeMillis() - t) + "ms.");
        }
    }

    private Map deserialize(File f) {
        Map stats = null;
        if (f.exists() && f.canRead()) {
            long t = System.currentTimeMillis();
            try {
                FileInputStream fis = new FileInputStream(f);
                try {
                    stats = (Map) (new XStream().fromXML(fis));

                    if (stats != null) {
                        // adjust stats data so that charts look realistic.
                        // we do that by ending the previous stats group with 0 value
                        // and starting the current stats group also with 0
                        // thus giving the chart nice plunge to zero indicating downtime
                        //
                        // and lets not bother about rotating stats;
                        // regular stats collection cycle will do it

                        for (Iterator it = stats.keySet().iterator(); it.hasNext();) {
                            List l = (List) stats.get(it.next());
                            if (l.size() > 0) {
                                XYDataItem xy = (XYDataItem) l.get(l.size() - 1);
                                l.add(new XYDataItem(xy.getX().longValue() + 1, 0));
                                l.add(new XYDataItem(System.currentTimeMillis(), 0));
                            }
                        }
                    }
                } finally {
                    fis.close();
                }
                logger.debug("stats data read in " + (System.currentTimeMillis() - t) + "ms.");
            } catch (Throwable e) {
                logger.error("Could not read stats data from " + f.getAbsolutePath(), e);
                //
                // make sure we always re-throw ThreadDeath
                //
                if (e instanceof ThreadDeath) {
                    throw (ThreadDeath) e;
                }
            }
        }

        return stats;
    }

    public void lockForUpdate() throws InterruptedException {
        lock.lockForUpdate();
    }

    public void releaseLock() {
        lock.releaseUpdateLock();
    }

    /**
     * Reads stats data from file on disk.
     *
     * @throws Exception
     */
    public synchronized void afterPropertiesSet() throws Exception {
        int index = 0;
        Map stats;

        while (true) {
            File f = index == 0 ? makeFile() : new File(makeFile().getAbsolutePath() + "." + index);
            stats = deserialize(f);
            index += 1;
            if (stats != null || index >= maxFiles - 1) {
                break;
            }
        }

        if (stats != null) {
            statsData = stats;
        } else {
            logger.debug("Stats data file not found. Empty file assumed.");
        }

    }

    public void destroy() throws Exception {
        serialize();
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        WebApplicationContext wac = (WebApplicationContext) applicationContext;
        contextTempDir = (File) wac.getServletContext().getAttribute("javax.servlet.context.tempdir");
    }
}