Java tutorial
/* * 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"); } }