/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 of * the License. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package org.ow2.proactive.jmx; import java.io.File; import java.io.IOException; import java.util.HashMap; import javax.management.MBeanAttributeInfo; import javax.management.StandardMBean; import org.apache.log4j.Logger; import org.ow2.proactive.utils.FileToBytesConverter; import org.rrd4j.ConsolFun; import org.rrd4j.DsType; import org.rrd4j.core.RrdDb; import org.rrd4j.core.RrdDef; import org.rrd4j.core.Sample; /** * This class (thread) dump all properties of MBean to RRD data base with specific period. */ public class RRDDataStore extends Thread { private static final int DEFAULT_STEP_IN_SECONDS = 4; private StandardMBean mbean; protected int step = DEFAULT_STEP_IN_SECONDS; //secs protected String dataBaseFile; protected final HashMap<String, String> dataSources = new HashMap<>(); protected volatile boolean terminate = false; protected Logger logger; protected RRDDataStore(String dataBaseFilePath, int step, Logger logger) { this.step = step; this.dataBaseFile = dataBaseFilePath; this.logger = logger; } /** * Initializes a new RRD data base if it's not exist. * * @param mbean is the source of chronological data * @param dataBaseFilePath is the path to the file with the rrd data base * @param step is the data base refresh period * @throws IOException is thrown when the data base exists but cannot be read */ public RRDDataStore(StandardMBean mbean, String dataBaseFilePath, int step, Logger logger) throws IOException { this(dataBaseFilePath, step, logger); this.mbean = mbean; for (MBeanAttributeInfo attrInfo : mbean.getMBeanInfo().getAttributes()) { try { if (attrInfo.isReadable() && mbean.getClass().getMethod("get" + attrInfo.getName()).getAnnotation(Chronological.class) != null) { String sourceName = attrInfo.getName(); if (sourceName.length() > 20) { // only 20 symbols allowed in data source name sourceName = sourceName.substring(0, 19); } dataSources.put(sourceName, attrInfo.getName()); } } catch (Exception e) { logger.error(e.getMessage(), e); } } initDatabase(); setName("RRD4J Data Store " + new File(dataBaseFilePath).getName()); setDaemon(true); start(); } protected void initDatabase() throws IOException { if (!new File(dataBaseFile).exists()) { if (step <= 0) { logger.debug("Provided step is invalid, forcing it to " + DEFAULT_STEP_IN_SECONDS); step = DEFAULT_STEP_IN_SECONDS; } logger.info("Node's statistics are saved in " + dataBaseFile); RrdDef rrdDef = new RrdDef(dataBaseFile, System.currentTimeMillis() / 1000, step); for (String dataSource : dataSources.keySet()) { rrdDef.addDatasource(dataSource, DsType.GAUGE, 600, 0, Double.NaN); } // for step equals 4 seconds // Archive of 10 minutes = 600 seconds (4 * 1 * 150) of completely detailed data rrdDef.addArchive(ConsolFun.AVERAGE, 0.5, 1, 150); // An archive of 1 hour = 3600 seconds (4 * 5 * 180) i.e. 180 averages of 5 steps rrdDef.addArchive(ConsolFun.AVERAGE, 0.5, 5, 180); // An archive of 4 hours = 14400 seconds (4 * 10 * 360) i.e. 360 averages of 10 steps rrdDef.addArchive(ConsolFun.AVERAGE, 0.5, 10, 360); // An archive of 8 hours = 28800 seconds (4 * 20 * 360) i.e. 360 averages of 20 steps rrdDef.addArchive(ConsolFun.AVERAGE, 0.5, 20, 360); // An archive of 24 hours = 86400 seconds (4 * 30 * 720) i.e. 720 averages of 30 steps rrdDef.addArchive(ConsolFun.AVERAGE, 0.5, 30, 720); // An archive of 1 week = 604800 seconds (4 * 210 * 720) i.e. 720 averages of 210 steps rrdDef.addArchive(ConsolFun.AVERAGE, 0.5, 210, 720); // An archive of 1 month ~= 28 days = 604800 seconds (4 * 840 * 720) i.e. 720 averages of 840 steps rrdDef.addArchive(ConsolFun.AVERAGE, 0.5, 840, 720); // An archive of 1 year = 364 days = 31449600 seconds (4 * 10920 * 720) i.e. 720 averages of 10920 steps rrdDef.addArchive(ConsolFun.AVERAGE, 0.5, 10920, 720); RrdDb dataBase = new RrdDb(rrdDef); dataBase.close(); } else { logger.info("Using existing RRD database: " + new File(dataBaseFile).getAbsolutePath()); } } /** * Periodically dumps the new mbean state to the data base */ public void run() { try { RrdDb dataBase = new RrdDb(dataBaseFile); Sample sample = dataBase.createSample(); logger.debug("RRD data base configuration:\n" + dataBase.getRrdDef().dump()); while (!terminate) { try { synchronized (dataSources) { dataSources.wait(step * 1000); if (terminate) { break; } // updating the data base for (String dataSource : dataSources.keySet()) { Object attrValue = mbean.getAttribute(dataSources.get(dataSource)); sample.setValue(dataSource, Double.parseDouble(attrValue.toString())); logger.debug(System.currentTimeMillis() / 1000 + " sampling: " + dataSource + " " + Double.parseDouble(attrValue.toString())); } sample.setTime(System.currentTimeMillis() / 1000); sample.update(); } } catch (Exception e) { logger.error(e.getMessage(), e); } } dataBase.close(); } catch (Exception e) { logger.error(e.getMessage(), e); } } /** * Converts the data base into the bytes array in order to send it to a client. * * @return bytes array * @throws IOException when file cannot be read */ public byte[] getBytes() throws IOException { synchronized (dataSources) { return FileToBytesConverter.convertFileToByteArray(new File(dataBaseFile)); } } /** * Terminates the thread activity. */ public void terminate() { synchronized (dataSources) { terminate = true; dataSources.notifyAll(); } } }