All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.purej.vminspect.data.statistics.rrd.JRobinImpl Maven / Gradle / Ivy

Go to download

An easy to use, feature-rich, JMX-based and embeddable Java VM monitoring tool with a web-based user-interface

There is a newer version: 2.1.1
Show newest version
package com.purej.vminspect.data.statistics.rrd;

import java.awt.Color;
import java.awt.GradientPaint;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.util.Calendar;
import org.jrobin.core.RrdBackend;
import org.jrobin.core.RrdBackendFactory;
import org.jrobin.core.RrdDb;
import org.jrobin.core.RrdDef;
import org.jrobin.core.RrdException;
import org.jrobin.core.RrdFileBackend;
import org.jrobin.core.RrdMemoryBackendFactory;
import org.jrobin.core.Sample;
import org.jrobin.core.Util;
import org.jrobin.graph.RrdGraph;
import org.jrobin.graph.RrdGraphDef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.purej.vminspect.data.statistics.Period;
import com.purej.vminspect.data.statistics.Range;
import com.purej.vminspect.util.Utils;

/**
 * JRobin based implementation of a RRD.
 *
 * @author Stefan Mueller
 */
public class JRobinImpl extends AbstractRrdImpl {
  private static final Logger LOG = LoggerFactory.getLogger(JRobinImpl.class);
  private static final String FUNCTION_AVG = "AVERAGE";
  private static final String FUNCTION_MAX = "MAX";

  private final RrdBackendFactory _rrdBackendFactory;
  private RrdDb _rrdDb; // Note: Might be reopend if broken...
  private RandomAccessFile _raf; // Underlying file or null, depending on RrdDb subclass

  /**
   * Creates a new instance of this class.
   *
   * @param name the name of the statistics, must be a simple name without spaces and special characters
   * @param storageDir where the RRD file should be stored
   * @param resolutionSeconds the resolution in seconds
   * @param rrdBackendFactory the backend factory to be used
   * @throws IOException if a RRD file access error occurred
   */
  public JRobinImpl(String name, String storageDir, int resolutionSeconds, Object rrdBackendFactory) throws IOException {
    super(name, storageDir, resolutionSeconds);
    _rrdBackendFactory = (RrdBackendFactory) Utils.checkNotNull(rrdBackendFactory);
    initRrdDb(false);
  }

  @Override
  public String getName() {
    return _name;
  }

  @Override
  public void addValue(double value) throws IOException {
    try {
      doAddValue(value);
    } catch (FileNotFoundException e) {
      LOG.warn("RRD file '" + _rrdPath + "' does not exist, recreating it...");
      initRrdDb(true);
      doAddValue(value);
    }
  }

  private void doAddValue(double value) throws IOException {
    try {
      // Create sample with the current timestamp:
      Sample sample = _rrdDb.createSample();
      if (sample.getTime() > _rrdDb.getLastUpdateTime()) {
        sample.setValue(0, value);
        sample.update();
        if (_raf != null) {
          _raf.getChannel().force(true);
        }
      }
    } catch (RrdException e) {
      String msg = "Accessing RRD statistics file '" + _rrdPath + "' failed! If the problem persists, delete the file so it will be recreated.";
      LOG.error(msg, e);
      throw new IOException(msg, e);
    }
  }

  @Override
  public byte[] createPng(String label, String unit, Range range, int width, int height) throws IOException {
    try {
      // Create the graph definition:
      RrdGraphDef graphDef = new RrdGraphDef();
      graphDef.setPoolUsed(true);
      graphDef.setFilename("-"); // Important for in-memory generation!

      // Set datasources:
      graphDef.datasource("average", _rrdPath, _name, FUNCTION_AVG, _rrdBackendFactory.getFactoryName());
      graphDef.datasource("max", _rrdPath, _name, FUNCTION_MAX, _rrdBackendFactory.getFactoryName());
      graphDef.setMinValue(0);

      // Set graphics stuff:
      graphDef.setImageFormat("png");
      graphDef.area("average", new GradientPaint(0, 0, Color.RED, 0, height, Color.GREEN, false), "Avg");
      graphDef.line("max", Color.BLUE, "Max");
      graphDef.gprint("average", FUNCTION_AVG, "Avg: %9.0f " + unit + "\\r");
      graphDef.gprint("max", FUNCTION_MAX, "Max: %9.0f " + unit + "\\r");
      graphDef.setWidth(width);
      graphDef.setHeight(height);

      setGraphStartEndTime(graphDef, range);
      graphDef.setTitle(getGraphTitle(label, range, width));

      return new RrdGraph(graphDef).getRrdGraphInfo().getBytes();
    } catch (final RrdException e) {
      throw new IOException(e);
    }
  }

  private RrdDef createRrdDef() throws RrdException {
    RrdDef rrdDef = new RrdDef(_rrdPath, _resolutionSeconds);
    rrdDef.setStartTime(Util.getTime() - _resolutionSeconds); // Matches more or less as collect is called right after init
    rrdDef.addDatasource(_name, "GAUGE", _resolutionSeconds * 2, 0, Double.NaN); // Single gauge

    // Archives for average/max for each supported period:
    // 1 second for 1 hour periods:
    rrdDef.addArchive(FUNCTION_AVG, 0.25, 1, DAY / _resolutionSeconds);
    rrdDef.addArchive(FUNCTION_MAX, 0.25, 1, DAY / _resolutionSeconds);
    // 1 hour for 1 week periods:
    rrdDef.addArchive(FUNCTION_AVG, 0.25, HOUR / _resolutionSeconds, 7 * 24);
    rrdDef.addArchive(FUNCTION_MAX, 0.25, HOUR / _resolutionSeconds, 7 * 24);
    // 6 hours for 1 Month periods:
    rrdDef.addArchive(FUNCTION_AVG, 0.25, 6 * HOUR / _resolutionSeconds, 31 * 4);
    rrdDef.addArchive(FUNCTION_MAX, 0.25, 6 * HOUR / _resolutionSeconds, 31 * 4);
    // 2 days for year/all periods:
    rrdDef.addArchive(FUNCTION_AVG, 0.25, 2 * DAY / _resolutionSeconds, 2 * 12 * 15);
    rrdDef.addArchive(FUNCTION_MAX, 0.25, 2 * DAY / _resolutionSeconds, 2 * 12 * 15);

    return rrdDef;
  }

  private void initRrdDb(boolean overwrite) throws IOException {
    try {
      _raf = null; // Reset underlying file...
      RrdDef def = createRrdDef();
      if (_rrdBackendFactory instanceof RrdMemoryBackendFactory) {
        _rrdDb = new RrdDb(def, _rrdBackendFactory);
      } else {
        // File backend:
        File rrdFile = new File(_rrdPath);
        if (overwrite || !rrdFile.exists() || rrdFile.length() == 0) {
          // Create a new one:
          _rrdDb = new RrdDb(def, _rrdBackendFactory);
        } else {
          // Open existing:
          _rrdDb = new RrdDb(_rrdPath, false, _rrdBackendFactory);
          // Sanity check - compare only step (eg. frequency) for now:
          if (def.getStep() != _rrdDb.getRrdDef().getStep()) {
            LOG.warn("Step size changed for {}, creating new one...", _rrdPath);
            _rrdDb.close();
            renameRrd(rrdFile);
            _rrdDb = new RrdDb(def, _rrdBackendFactory);
          }
        }

        // Try get the underlying RandomAccessFile:
        RrdBackend backend = _rrdDb.getRrdBackend();
        if (backend.getClass() == RrdFileBackend.class) { // No instanceof check as of subclasses to be ignored...
          try {
            Field f = RrdFileBackend.class.getDeclaredField("file");
            f.setAccessible(true);
            _raf = (RandomAccessFile) f.get(backend);
          } catch (Exception e) {
            LOG.warn("Cannot get underlying RandomAccessFile of RrdBackend, no explicit file sync/flush will be performed!");
          }
        }
      }
    } catch (RrdException e) {
      throw new IOException(e);
    }
  }

  private static void setGraphStartEndTime(RrdGraphDef graphDef, Range range) {
    long endTime; // Current timestamp or custom end-date
    long startTime; // Depending on the range and end-date
    if (range.getPeriod().equals(Period.CUSTOM)) {
      // Custom period:
      endTime = Math.min(range.getEndDate().getTime() / 1000, Util.getTime());
      startTime = range.getStartDate().getTime() / 1000;
    } else {
      endTime = Util.getTime();
      startTime = endTime - range.getPeriod().getDurationSeconds();
    }
    graphDef.setStartTime(startTime);
    graphDef.setEndTime(endTime);
    graphDef.setFirstDayOfWeek(Calendar.getInstance().getFirstDayOfWeek());
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy