Package org.jrobin.core

Source Code of org.jrobin.core.FetchData

/*******************************************************************************
* Copyright (c) 2001-2005 Sasa Markovic and Ciaran Treanor.
* Copyright (c) 2011 The OpenNMS Group, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*******************************************************************************/

package org.jrobin.core;

import org.jrobin.data.Aggregates;
import org.jrobin.data.DataProcessor;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
* Class used to represent data fetched from the RRD.
* Object of this class is created when the method
* {@link FetchRequest#fetchData() fetchData()} is
* called on a {@link FetchRequest FetchRequest} object.<p>
* <p/>
* Data returned from the RRD is, simply, just one big table filled with
* timestamps and corresponding datasource values.
* Use {@link #getRowCount() getRowCount()} method to count the number
* of returned timestamps (table rows).<p>
* <p/>
* The first table column is filled with timestamps. Time intervals
* between consecutive timestamps are guaranteed to be equal. Use
* {@link #getTimestamps() getTimestamps()} method to get an array of
* timestamps returned.<p>
* <p/>
* Remaining columns are filled with datasource values for the whole timestamp range,
* on a column-per-datasource basis. Use {@link #getColumnCount() getColumnCount()} to find
* the number of datasources and {@link #getValues(int) getValues(i)} method to obtain
* all values for the i-th datasource. Returned datasource values correspond to
* the values returned with {@link #getTimestamps() getTimestamps()} method.<p>
*/
public class FetchData implements ConsolFuns {
  // anything fuuny will do
  private static final String RPN_SOURCE_NAME = "WHERE THE SPEECHLES UNITE IN A SILENT ACCORD";

  private FetchRequest request;
  private String[] dsNames;
  private long[] timestamps;
  private double[][] values;

  private Archive matchingArchive;
  private long arcStep;
  private long arcEndTime;

  FetchData(Archive matchingArchive, FetchRequest request) throws IOException {
    this.matchingArchive = matchingArchive;
    this.arcStep = matchingArchive.getArcStep();
    this.arcEndTime = matchingArchive.getEndTime();
    this.dsNames = request.getFilter();
    if (this.dsNames == null) {
      this.dsNames = matchingArchive.getParentDb().getDsNames();
    }
    this.request = request;
  }

  void setTimestamps(long[] timestamps) {
    this.timestamps = timestamps;
  }

  void setValues(double[][] values) {
    this.values = values;
  }

  /**
   * Returns the number of rows fetched from the corresponding RRD.
   * Each row represents datasource values for the specific timestamp.
   *
   * @return Number of rows.
   */
  public int getRowCount() {
    return timestamps.length;
  }

  /**
   * Returns the number of columns fetched from the corresponding RRD.
   * This number is always equal to the number of datasources defined
   * in the RRD. Each column represents values of a single datasource.
   *
   * @return Number of columns (datasources).
   */
  public int getColumnCount() {
    return dsNames.length;
  }

  /**
   * Returns an array of timestamps covering the whole range specified in the
   * {@link FetchRequest FetchReguest} object.
   *
   * @return Array of equidistant timestamps.
   */
  public long[] getTimestamps() {
    return timestamps;
  }

  /**
   * Returns the step with which this data was fetched.
   *
   * @return Step as long.
   */
  public long getStep() {
    return timestamps[1] - timestamps[0];
  }

  /**
   * Returns all archived values for a single datasource.
   * Returned values correspond to timestamps
   * returned with {@link #getTimestamps() getTimestamps()} method.
   *
   * @param dsIndex Datasource index.
   * @return Array of single datasource values.
   */
  public double[] getValues(int dsIndex) {
    return values[dsIndex];
  }

  /**
   * Returns all archived values for all datasources.
   * Returned values correspond to timestamps
   * returned with {@link #getTimestamps() getTimestamps()} method.
   *
   * @return Two-dimensional aray of all datasource values.
   */
  public double[][] getValues() {
    return values;
  }

  /**
   * Returns all archived values for a single datasource.
   * Returned values correspond to timestamps
   * returned with {@link #getTimestamps() getTimestamps()} method.
   *
   * @param dsName Datasource name.
   * @return Array of single datasource values.
   * @throws RrdException Thrown if no matching datasource name is found.
   */
  public double[] getValues(String dsName) throws RrdException {
    for (int dsIndex = 0; dsIndex < getColumnCount(); dsIndex++) {
      if (dsName.equals(dsNames[dsIndex])) {
        return getValues(dsIndex);
      }
    }
    throw new RrdException("Datasource [" + dsName + "] not found");
  }

  /**
   * Returns a set of values created by applying RPN expression to the fetched data.
   * For example, if you have two datasources named <code>x</code> and <code>y</code>
   * in this FetchData and you want to calculate values for <code>(x+y)/2<code> use something like: <p>
   * <code>getRpnValues("x,y,+,2,/");</code><p>
   *
   * @param rpnExpression RRDTool-like RPN expression
   * @return Calculated values
   * @throws RrdException Thrown if invalid RPN expression is supplied
   */
  public double[] getRpnValues(String rpnExpression) throws RrdException {
    DataProcessor dataProcessor = createDataProcessor(rpnExpression);
    return dataProcessor.getValues(RPN_SOURCE_NAME);
  }

  /**
   * Returns {@link FetchRequest FetchRequest} object used to create this FetchData object.
   *
   * @return Fetch request object.
   */
  public FetchRequest getRequest() {
    return request;
  }

  /**
   * Returns the first timestamp in this FetchData object.
   *
   * @return The smallest timestamp.
   */
  public long getFirstTimestamp() {
    return timestamps[0];
  }

  /**
   * Returns the last timestamp in this FecthData object.
   *
   * @return The biggest timestamp.
   */
  public long getLastTimestamp() {
    return timestamps[timestamps.length - 1];
  }

  /**
   * Returns Archive object which is determined to be the best match for the
   * timestamps specified in the fetch request. All datasource values are obtained
   * from round robin archives belonging to this archive.
   *
   * @return Matching archive.
   */
  public Archive getMatchingArchive() {
    return matchingArchive;
  }

  /**
   * Returns array of datasource names found in the corresponding RRD. If the request
   * was filtered (data was fetched only for selected datasources), only datasources selected
   * for fetching are returned.
   *
   * @return Array of datasource names.
   */
  public String[] getDsNames() {
    return dsNames;
  }

  /**
   * Retrieve the table index number of a datasource by name.  Names are case sensitive.
   *
   * @param dsName Name of the datasource for which to find the index.
   * @return Index number of the datasources in the value table.
   */
  public int getDsIndex(String dsName) {
    // Let's assume the table of dsNames is always small, so it is not necessary to use a hashmap for lookups
    for (int i = 0; i < dsNames.length; i++) {
      if (dsNames[i].equals(dsName)) {
        return i;
      }
    }
    return -1;    // Datasource not found !
  }

  /**
   * Dumps the content of the whole FetchData object. Useful for debugging.
   */
  public String dump() {
    StringBuffer buffer = new StringBuffer("");
    for (int row = 0; row < getRowCount(); row++) {
      buffer.append(timestamps[row]);
      buffer.append(":  ");
      for (int dsIndex = 0; dsIndex < getColumnCount(); dsIndex++) {
        buffer.append(Util.formatDouble(values[dsIndex][row], true));
        buffer.append("  ");
      }
      buffer.append("\n");
    }
    return buffer.toString();
  }

  /**
   * Returns string representing fetched data in a RRDTool-like form.
   *
   * @return Fetched data as a string in a rrdfetch-like output form.
   */
  public String toString() {
    // print header row
    StringBuffer buff = new StringBuffer();
    buff.append(padWithBlanks("", 10));
    buff.append(" ");
    for (String dsName : dsNames) {
      buff.append(padWithBlanks(dsName, 18));
    }
    buff.append("\n \n");
    for (int i = 0; i < timestamps.length; i++) {
      buff.append(padWithBlanks("" + timestamps[i], 10));
      buff.append(":");
      for (int j = 0; j < dsNames.length; j++) {
        double value = values[j][i];
        String valueStr = Double.isNaN(value) ? "nan" : Util.formatDouble(value);
        buff.append(padWithBlanks(valueStr, 18));
      }
      buff.append("\n");
    }
    return buff.toString();
  }

  private static String padWithBlanks(String input, int width) {
    StringBuffer buff = new StringBuffer("");
    int diff = width - input.length();
    while (diff-- > 0) {
      buff.append(' ');
    }
    buff.append(input);
    return buff.toString();
  }

  /**
   * Returns single aggregated value from the fetched data for a single datasource.
   *
   * @param dsName  Datasource name
   * @param consolFun Consolidation function to be applied to fetched datasource values.
   *                  Valid consolidation functions are "MIN", "MAX", "LAST", "FIRST", "AVERAGE" and "TOTAL"
   *                  (these string constants are conveniently defined in the {@link ConsolFuns} class)
   * @return MIN, MAX, LAST, FIRST, AVERAGE or TOTAL value calculated from the fetched data
   *         for the given datasource name
   * @throws RrdException Thrown if the given datasource name cannot be found in fetched data.
   */
  public double getAggregate(String dsName, String consolFun) throws RrdException {
    DataProcessor dp = createDataProcessor(null);
    return dp.getAggregate(dsName, consolFun);
  }

  /**
   * Returns aggregated value from the fetched data for a single datasource.
   * Before applying aggregation functions, specified RPN expression is applied to fetched data.
   * For example, if you have a gauge datasource named 'foots' but you want to find the maximum
   * fetched value in meters use something like: <p>
   * <code>getAggregate("foots", "MAX", "foots,0.3048,*");</code><p>
   *
   * @param dsName    Datasource name
   * @param consolFun   Consolidation function (MIN, MAX, LAST, FIRST, AVERAGE or TOTAL)
   * @param rpnExpression RRDTool-like RPN expression
   * @return Aggregated value
   * @throws RrdException Thrown if the given datasource name cannot be found in fetched data, or if
   *                      invalid RPN expression is supplied
   * @throws IOException  Thrown in case of I/O error (unlikely to happen)
   * @deprecated This method is preserved just for backward compatibility.
   */
  public double getAggregate(String dsName, String consolFun, String rpnExpression)
      throws RrdException, IOException {
    // for backward compatibility
    rpnExpression = rpnExpression.replaceAll("value", dsName);
    return getRpnAggregate(rpnExpression, consolFun);
  }

  /**
   * Returns aggregated value for a set of values calculated by applying an RPN expression to the
   * fetched data. For example, if you have two datasources named <code>x</code> and <code>y</code>
   * in this FetchData and you want to calculate MAX value of <code>(x+y)/2<code> use something like: <p>
   * <code>getRpnAggregate("x,y,+,2,/", "MAX");</code><p>
   *
   * @param rpnExpression RRDTool-like RPN expression
   * @param consolFun   Consolidation function (MIN, MAX, LAST, FIRST, AVERAGE or TOTAL)
   * @return Aggregated value
   * @throws RrdException Thrown if invalid RPN expression is supplied
   */
  public double getRpnAggregate(String rpnExpression, String consolFun) throws RrdException {
    DataProcessor dataProcessor = createDataProcessor(rpnExpression);
    return dataProcessor.getAggregate(RPN_SOURCE_NAME, consolFun);
  }

  /**
   * Returns all aggregated values (MIN, MAX, LAST, FIRST, AVERAGE or TOTAL) calculated from the fetched data
   * for a single datasource.
   *
   * @param dsName Datasource name.
   * @return Simple object containing all aggregated values.
   * @throws RrdException Thrown if the given datasource name cannot be found in the fetched data.
   */
  public Aggregates getAggregates(String dsName) throws RrdException {
    DataProcessor dataProcessor = createDataProcessor(null);
    return dataProcessor.getAggregates(dsName);
  }

  /**
   * Returns all aggregated values for a set of values calculated by applying an RPN expression to the
   * fetched data. For example, if you have two datasources named <code>x</code> and <code>y</code>
   * in this FetchData and you want to calculate MIN, MAX, LAST, FIRST, AVERAGE and TOTAL value
   * of <code>(x+y)/2<code> use something like: <p>
   * <code>getRpnAggregates("x,y,+,2,/");</code><p>
   *
   * @param rpnExpression RRDTool-like RPN expression
   * @return Object containing all aggregated values
   * @throws RrdException Thrown if invalid RPN expression is supplied
   */
  public Aggregates getRpnAggregates(String rpnExpression) throws RrdException, IOException {
    DataProcessor dataProcessor = createDataProcessor(rpnExpression);
    return dataProcessor.getAggregates(RPN_SOURCE_NAME);
  }

  /**
   * Used by ISPs which charge for bandwidth utilization on a "95th percentile" basis.<p>
   * <p/>
   * The 95th percentile is the highest source value left when the top 5% of a numerically sorted set
   * of source data is discarded. It is used as a measure of the peak value used when one discounts
   * a fair amount for transitory spikes. This makes it markedly different from the average.<p>
   * <p/>
   * Read more about this topic at:<p>
   * <a href="http://www.red.net/support/resourcecentre/leasedline/percentile.php">Rednet</a> or<br>
   * <a href="http://www.bytemark.co.uk/support/tech/95thpercentile.html">Bytemark</a>.
   *
   * @param dsName Datasource name
   * @return 95th percentile of fetched source values
   * @throws RrdException Thrown if invalid source name is supplied
   */
  public double get95Percentile(String dsName) throws RrdException {
    DataProcessor dataProcessor = createDataProcessor(null);
    return dataProcessor.get95Percentile(dsName);
  }

  /**
   * Same as {@link #get95Percentile(String)}, but for a set of values calculated with the given
   * RPN expression.
   *
   * @param rpnExpression RRDTool-like RPN expression
   * @return 95-percentile
   * @throws RrdException Thrown if invalid RPN expression is supplied
   */
  public double getRpn95Percentile(String rpnExpression) throws RrdException {
    DataProcessor dataProcessor = createDataProcessor(rpnExpression);
    return dataProcessor.get95Percentile(RPN_SOURCE_NAME);
  }

  /**
   * Dumps fetch data to output stream in XML format.
   *
   * @param outputStream Output stream to dump fetch data to
   * @throws IOException Thrown in case of I/O error
   */
  public void exportXml(OutputStream outputStream) throws IOException {
    XmlWriter writer = new XmlWriter(outputStream);
    writer.startTag("fetch_data");
    writer.startTag("request");
    writer.writeTag("file", request.getParentDb().getPath());
    writer.writeComment(Util.getDate(request.getFetchStart()));
    writer.writeTag("start", request.getFetchStart());
    writer.writeComment(Util.getDate(request.getFetchEnd()));
    writer.writeTag("end", request.getFetchEnd());
    writer.writeTag("resolution", request.getResolution());
    writer.writeTag("cf", request.getConsolFun());
    writer.closeTag(); // request
    writer.startTag("datasources");
    for (String dsName : dsNames) {
      writer.writeTag("name", dsName);
    }
    writer.closeTag(); // datasources
    writer.startTag("data");
    for (int i = 0; i < timestamps.length; i++) {
      writer.startTag("row");
      writer.writeComment(Util.getDate(timestamps[i]));
      writer.writeTag("timestamp", timestamps[i]);
      writer.startTag("values");
      for (int j = 0; j < dsNames.length; j++) {
        writer.writeTag("v", values[j][i]);
      }
      writer.closeTag(); // values
      writer.closeTag(); // row
    }
    writer.closeTag(); // data
    writer.closeTag(); // fetch_data
    writer.flush();
  }

  /**
   * Dumps fetch data to file in XML format.
   *
   * @param filepath Path to destination file
   * @throws IOException Thrown in case of I/O error
   */
  public void exportXml(String filepath) throws IOException {
    OutputStream outputStream = null;
    try {
      outputStream = new FileOutputStream(filepath);
      exportXml(outputStream);
    }
    finally {
      if (outputStream != null) {
        outputStream.close();
      }
    }
  }

  /**
   * Dumps fetch data in XML format.
   *
   * @return String containing XML formatted fetch data
   * @throws IOException Thrown in case of I/O error
   */
  public String exportXml() throws IOException {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    exportXml(outputStream);
    return outputStream.toString();
  }

  /**
   * Returns the step of the corresponding RRA archive
   *
   * @return Archive step in seconds
   */
  public long getArcStep() {
    return arcStep;
  }

  /**
   * Returns the timestamp of the last populated slot in the corresponding RRA archive
   *
   * @return Timestamp in seconds
   */
  public long getArcEndTime() {
    return arcEndTime;
  }

  private DataProcessor createDataProcessor(String rpnExpression) throws RrdException {
    DataProcessor dataProcessor = new DataProcessor(request.getFetchStart(), request.getFetchEnd());
    for (String dsName : dsNames) {
      dataProcessor.addDatasource(dsName, this);
    }
    if (rpnExpression != null) {
      dataProcessor.addDatasource(RPN_SOURCE_NAME, rpnExpression);
      try {
        dataProcessor.processData();
      }
      catch (IOException ioe) {
        // highly unlikely, since all datasources have already calculated values
        throw new RuntimeException("Impossible error: " + ioe);
      }
    }
    return dataProcessor;
  }
}
TOP

Related Classes of org.jrobin.core.FetchData

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.