org.jrobin.core.RrdDb Maven / Gradle / Ivy
Show all versions of jrobin Show documentation
/*******************************************************************************
* 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 java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
/**
* Main class used to create and manipulate round robin databases (RRDs). Use this class to perform
* update and fetch operations on exisiting RRDs, to create new RRD from
* the definition (object of class {@link org.jrobin.core.RrdDef RrdDef}) or
* from XML file (dumped content of RRDTool's or JRobin's RRD file).
*
* Each RRD is backed with some kind of storage. For example, RRDTool supports only one kind of
* storage (disk file). On the contrary, JRobin gives you freedom to use other storage (backend) types
* even to create your own backend types for some special purposes. JRobin by default stores
* RRD data in files (as RRDTool), but you might choose to store RRD data in memory (this is
* supported in JRobin), to use java.nio.* instead of java.io.* package for file manipulation
* (also supported) or to store whole RRDs in the SQL database
* (you'll have to extend some classes to do this).
*
* Note that JRobin uses binary format different from RRDTool's format. You cannot
* use this class to manipulate RRD files created with RRDTool. However, if you perform
* the same sequence of create, update and fetch operations, you will get exactly the same
* results from JRobin and RRDTool.
*
* You will not be able to use JRobin API if you are not familiar with
* basic RRDTool concepts. Good place to start is the
* official RRD tutorial
* and relevant RRDTool man pages: rrdcreate,
* rrdupdate,
* rrdfetch and
* rrdgraph.
* For RRDTool's advanced graphing capabilities (RPN extensions), also supported in JRobin,
* there is an excellent
* CDEF tutorial.
*
*
* @see RrdBackend
* @see RrdBackendFactory
*/
public class RrdDb implements RrdUpdater {
/**
* prefix to identify external XML file source used in various RrdDb constructors
*/
public static final String PREFIX_XML = "xml:/";
/**
* prefix to identify external RRDTool file source used in various RrdDb constructors
*/
public static final String PREFIX_RRDTool = "rrdtool:/";
// static final String RRDTOOL = "rrdtool";
static final int XML_INITIAL_BUFFER_CAPACITY = 100000; // bytes
private RrdBackend backend;
private RrdAllocator allocator = new RrdAllocator();
private Header header;
private Datasource[] datasources;
private Archive[] archives;
private boolean closed = false;
/**
* Constructor used to create new RRD object from the definition. This RRD object will be backed
* with a storage (backend) of the default type. Initially, storage type defaults to "NIO"
* (RRD bytes will be put in a file on the disk). Default storage type can be changed with a static
* {@link RrdBackendFactory#setDefaultFactory(String)} method call.
*
* New RRD file structure is specified with an object of class
* {@link org.jrobin.core.RrdDef RrdDef}. The underlying RRD storage is created as soon
* as the constructor returns.
*
* Typical scenario:
*
*
* // create new RRD definition
* RrdDef def = new RrdDef("test.rrd", 300);
* def.addDatasource("input", DsTypes.DT_COUNTER, 600, 0, Double.NaN);
* def.addDatasource("output", DsTypes.DT_COUNTER, 600, 0, Double.NaN);
* def.addArchive(ConsolFuns.CF_AVERAGE, 0.5, 1, 600);
* def.addArchive(ConsolFuns.CF_AVERAGE, 0.5, 6, 700);
* def.addArchive(ConsolFuns.CF_AVERAGE, 0.5, 24, 797);
* def.addArchive(ConsolFuns.CF_AVERAGE, 0.5, 288, 775);
* def.addArchive(ConsolFuns.CF_MAX, 0.5, 1, 600);
* def.addArchive(ConsolFuns.CF_MAX, 0.5, 6, 700);
* def.addArchive(ConsolFuns.CF_MAX, 0.5, 24, 797);
* def.addArchive(ConsolFuns.CF_MAX, 0.5, 288, 775);
*
* // RRD definition is now completed, create the database!
* RrdDb rrd = new RrdDb(def);
* // new RRD file has been created on your disk
*
*
* @param rrdDef Object describing the structure of the new RRD file.
* @throws IOException Thrown in case of I/O error.
* @throws RrdException Thrown if invalid RrdDef object is supplied.
*/
public RrdDb(RrdDef rrdDef) throws RrdException, IOException {
this(rrdDef, RrdFileBackendFactory.getDefaultFactory());
}
/**
* Constructor used to create new RRD object from the definition object but with a storage
* (backend) different from default.
*
* JRobin uses factories to create RRD backend objecs. There are three different
* backend factories supplied with JRobin, and each factory has its unique name:
*
*
* - FILE: backends created from this factory will store RRD data to files by using
* java.io.* classes and methods
*
- NIO: backends created from this factory will store RRD data to files by using
* java.nio.* classes and methods
*
- MEMORY: backends created from this factory will store RRD data in memory. This might
* be useful in runtime environments which prohibit disk utilization, or for storing temporary,
* non-critical data (it gets lost as soon as JVM exits).
*
*
* For example, to create RRD in memory, use the following code:
*
* RrdBackendFactory factory = RrdBackendFactory.getFactory("MEMORY");
* RrdDb rrdDb = new RrdDb(rrdDef, factory);
* rrdDb.close();
*
*
* New RRD file structure is specified with an object of class
* {@link org.jrobin.core.RrdDef RrdDef}. The underlying RRD storage is created as soon
* as the constructor returns.
*
* @param rrdDef RRD definition object
* @param factory The factory which will be used to create storage for this RRD
* @throws RrdException Thrown if invalid factory or definition is supplied
* @throws IOException Thrown in case of I/O error
* @see RrdBackendFactory
*/
public RrdDb(RrdDef rrdDef, RrdBackendFactory factory) throws RrdException, IOException {
rrdDef.validate();
String path = rrdDef.getPath();
backend = factory.open(path, false);
try {
backend.setLength(rrdDef.getEstimatedSize());
// create header
header = new Header(this, rrdDef);
// create datasources
DsDef[] dsDefs = rrdDef.getDsDefs();
datasources = new Datasource[dsDefs.length];
for (int i = 0; i < dsDefs.length; i++) {
datasources[i] = new Datasource(this, dsDefs[i]);
}
// create archives
ArcDef[] arcDefs = rrdDef.getArcDefs();
archives = new Archive[arcDefs.length];
for (int i = 0; i < arcDefs.length; i++) {
archives[i] = new Archive(this, arcDefs[i]);
}
}
catch (IOException e) {
backend.close();
throw e;
}
}
/**
* Constructor used to open already existing RRD. This RRD object will be backed
* with a storage (backend) of the default type (file on the disk). Constructor
* obtains read or read/write access to this RRD.
*
* @param path Path to existing RRD.
* @param readOnly Should be set to false
if you want to update
* the underlying RRD. If you want just to fetch data from the RRD file
* (read-only access), specify true
. If you try to update RRD file
* open in read-only mode (m_readOnly
set to true
),
* IOException
will be thrown.
* @throws IOException Thrown in case of I/O error.
* @throws RrdException Thrown in case of JRobin specific error.
*/
public RrdDb(String path, boolean readOnly) throws IOException, RrdException {
this(path, readOnly, RrdBackendFactory.getDefaultFactory());
}
/**
* Constructor used to open already existing RRD backed
* with a storage (backend) different from default. Constructor
* obtains read or read/write access to this RRD.
*
* @param path Path to existing RRD.
* @param readOnly Should be set to false
if you want to update
* the underlying RRD. If you want just to fetch data from the RRD file
* (read-only access), specify true
. If you try to update RRD file
* open in read-only mode (m_readOnly
set to true
),
* IOException
will be thrown.
* @param factory Backend factory which will be used for this RRD.
* @throws FileNotFoundException Thrown if the requested file does not exist.
* @throws IOException Thrown in case of general I/O error (bad RRD file, for example).
* @throws RrdException Thrown in case of JRobin specific error.
* @see RrdBackendFactory
*/
public RrdDb(String path, boolean readOnly, RrdBackendFactory factory)
throws FileNotFoundException, IOException, RrdException {
// opens existing RRD file - throw exception if the file does not exist...
if (!factory.exists(path)) {
throw new FileNotFoundException("Could not open " + path + " [non existent]");
}
backend = factory.open(path, readOnly);
try {
// restore header
header = new Header(this, (RrdDef) null);
header.validateHeader();
// restore datasources
int dsCount = header.getDsCount();
datasources = new Datasource[dsCount];
for (int i = 0; i < dsCount; i++) {
datasources[i] = new Datasource(this, null);
}
// restore archives
int arcCount = header.getArcCount();
archives = new Archive[arcCount];
for (int i = 0; i < arcCount; i++) {
archives[i] = new Archive(this, null);
}
}
catch (RrdException e) {
backend.close();
throw e;
}
catch (IOException e) {
backend.close();
throw e;
}
}
/**
*
Constructor used to open already existing RRD in R/W mode, with a default storage
* (backend) type (file on the disk).
*
* @param path Path to existing RRD.
* @throws IOException Thrown in case of I/O error.
* @throws RrdException Thrown in case of JRobin specific error.
*/
public RrdDb(String path) throws IOException, RrdException {
this(path, false);
}
/**
*
Constructor used to open already existing RRD in R/W mode with a storage (backend) type
* different from default.
*
* @param path Path to existing RRD.
* @param factory Backend factory used to create this RRD.
* @throws IOException Thrown in case of I/O error.
* @throws RrdException Thrown in case of JRobin specific error.
* @see RrdBackendFactory
*/
public RrdDb(String path, RrdBackendFactory factory) throws IOException, RrdException {
this(path, false, factory);
}
/**
* Constructor used to create RRD files from external file sources.
* Supported external file sources are:
*
*
* - RRDTool/JRobin XML file dumps (i.e files created with
rrdtool dump
command).
* - RRDTool binary files.
*
*
* Newly created RRD will be backed with a default storage (backend) type
* (file on the disk).
*
* JRobin and RRDTool use the same format for XML dump and this constructor should be used to
* (re)create JRobin RRD files from XML dumps. First, dump the content of a RRDTool
* RRD file (use command line):
*
*
* rrdtool dump original.rrd > original.xml
*
*
* Than, use the file original.xml
to create JRobin RRD file named
* copy.rrd
:
*
*
* RrdDb rrd = new RrdDb("copy.rrd", "original.xml");
*
*
* or:
*
*
* RrdDb rrd = new RrdDb("copy.rrd", "xml:/original.xml");
*
*
* See documentation for {@link #dumpXml(java.lang.String) dumpXml()} method
* to see how to convert JRobin files to RRDTool's format.
*
* To read RRDTool files directly, specify rrdtool:/
prefix in the
* externalPath
argument. For example, to create JRobin compatible file named
* copy.rrd
from the file original.rrd
created with RRDTool, use
* the following code:
*
*
* RrdDb rrd = new RrdDb("copy.rrd", "rrdtool:/original.rrd");
*
*
* Note that the prefix xml:/
or rrdtool:/
is necessary to distinguish
* between XML and RRDTool's binary sources. If no prefix is supplied, XML format is assumed.
*
* @param rrdPath Path to a RRD file which will be created
* @param externalPath Path to an external file which should be imported, with an optional
* xml:/
or rrdtool:/
prefix.
* @throws IOException Thrown in case of I/O error
* @throws RrdException Thrown in case of JRobin specific error
*/
public RrdDb(String rrdPath, String externalPath) throws IOException, RrdException, RrdException {
this(rrdPath, externalPath, RrdBackendFactory.getDefaultFactory());
}
/**
* Constructor used to create RRD files from external file sources with a backend type
* different from default. Supported external file sources are:
*
*
* - RRDTool/JRobin XML file dumps (i.e files created with
rrdtool dump
command).
* - RRDTool binary files.
*
*
* JRobin and RRDTool use the same format for XML dump and this constructor should be used to
* (re)create JRobin RRD files from XML dumps. First, dump the content of a RRDTool
* RRD file (use command line):
*
*
* rrdtool dump original.rrd > original.xml
*
*
* Than, use the file original.xml
to create JRobin RRD file named
* copy.rrd
:
*
*
* RrdDb rrd = new RrdDb("copy.rrd", "original.xml");
*
*
* or:
*
*
* RrdDb rrd = new RrdDb("copy.rrd", "xml:/original.xml");
*
*
* See documentation for {@link #dumpXml(java.lang.String) dumpXml()} method
* to see how to convert JRobin files to RRDTool's format.
*
* To read RRDTool files directly, specify rrdtool:/
prefix in the
* externalPath
argument. For example, to create JRobin compatible file named
* copy.rrd
from the file original.rrd
created with RRDTool, use
* the following code:
*
*
* RrdDb rrd = new RrdDb("copy.rrd", "rrdtool:/original.rrd");
*
*
* Note that the prefix xml:/
or rrdtool:/
is necessary to distinguish
* between XML and RRDTool's binary sources. If no prefix is supplied, XML format is assumed.
*
* @param rrdPath Path to RRD which will be created
* @param externalPath Path to an external file which should be imported, with an optional
* xml:/
or rrdtool:/
prefix.
* @param factory Backend factory which will be used to create storage (backend) for this RRD.
* @throws IOException Thrown in case of I/O error
* @throws RrdException Thrown in case of JRobin specific error
* @see RrdBackendFactory
*/
public RrdDb(String rrdPath, String externalPath, RrdBackendFactory factory)
throws IOException, RrdException {
DataImporter reader;
if (externalPath.startsWith(PREFIX_RRDTool)) {
String rrdToolPath = externalPath.substring(PREFIX_RRDTool.length());
reader = new RrdToolReader(rrdToolPath);
}
else if (externalPath.startsWith(PREFIX_XML)) {
externalPath = externalPath.substring(PREFIX_XML.length());
reader = new XmlReader(externalPath);
}
else {
reader = new XmlReader(externalPath);
}
backend = factory.open(rrdPath, false);
try {
backend.setLength(reader.getEstimatedSize());
// create header
header = new Header(this, reader);
// create datasources
datasources = new Datasource[reader.getDsCount()];
for (int i = 0; i < datasources.length; i++) {
datasources[i] = new Datasource(this, reader, i);
}
// create archives
archives = new Archive[reader.getArcCount()];
for (int i = 0; i < archives.length; i++) {
archives[i] = new Archive(this, reader, i);
}
reader.release();
}
catch (RrdException e) {
backend.close();
throw e;
}
catch (IOException e) {
backend.close();
throw e;
}
}
public RrdDb(final File file) throws IOException, RrdException {
this(file.getPath());
}
public RrdDb(final File file, final boolean readOnly) throws IOException, RrdException {
this(file.getPath(), readOnly);
}
/**
* Closes RRD. No further operations are allowed on this RrdDb object.
*
* @throws IOException Thrown in case of I/O related error.
*/
public synchronized void close() throws IOException {
if (!closed) {
closed = true;
backend.close();
}
}
/**
* Returns true if the RRD is closed.
*
* @return true if closed, false otherwise
*/
public boolean isClosed() {
return closed;
}
/**
* Returns RRD header.
*
* @return Header object
*/
public Header getHeader() {
return header;
}
/**
* Returns Datasource object for the given datasource index.
*
* @param dsIndex Datasource index (zero based)
* @return Datasource object
*/
public Datasource getDatasource(int dsIndex) {
return datasources[dsIndex];
}
/**
* Returns Archive object for the given archive index.
*
* @param arcIndex Archive index (zero based)
* @return Archive object
*/
public Archive getArchive(int arcIndex) {
return archives[arcIndex];
}
/**
* Returns an array of datasource names defined in RRD.
*
* @return Array of datasource names.
* @throws IOException Thrown in case of I/O error.
*/
public String[] getDsNames() throws IOException {
int n = datasources.length;
String[] dsNames = new String[n];
for (int i = 0; i < n; i++) {
dsNames[i] = datasources[i].getDsName();
}
return dsNames;
}
/**
* Creates new sample with the given timestamp and all datasource values set to
* 'unknown'. Use returned Sample
object to specify
* datasource values for the given timestamp. See documentation for
* {@link org.jrobin.core.Sample Sample} for an explanation how to do this.
*
* Once populated with data source values, call Sample's
* {@link org.jrobin.core.Sample#update() update()} method to actually
* store sample in the RRD associated with it.
*
* @param time Sample timestamp rounded to the nearest second (without milliseconds).
* @return Fresh sample with the given timestamp and all data source values set to 'unknown'.
* @throws IOException Thrown in case of I/O error.
*/
public Sample createSample(long time) throws IOException {
return new Sample(this, time);
}
/**
* Creates new sample with the current timestamp and all data source values set to
* 'unknown'. Use returned Sample
object to specify
* datasource values for the current timestamp. See documentation for
* {@link org.jrobin.core.Sample Sample} for an explanation how to do this.
*
* Once populated with data source values, call Sample's
* {@link org.jrobin.core.Sample#update() update()} method to actually
* store sample in the RRD associated with it.
*
* @return Fresh sample with the current timestamp and all
* data source values set to 'unknown'.
* @throws IOException Thrown in case of I/O error.
*/
public Sample createSample() throws IOException {
return createSample(Util.getTime());
}
/**
*
Prepares fetch request to be executed on this RRD. Use returned
* FetchRequest
object and its {@link org.jrobin.core.FetchRequest#fetchData() fetchData()}
* method to actually fetch data from the RRD file.
*
* @param consolFun Consolidation function to be used in fetch request. Allowed values are
* "AVERAGE", "MIN", "MAX" and "LAST" (these constants are conveniently defined in the
* {@link ConsolFuns} class).
* @param fetchStart Starting timestamp for fetch request.
* @param fetchEnd Ending timestamp for fetch request.
* @param resolution Fetch resolution (see RRDTool's
* rrdfetch man page for an
* explanation of this parameter.
* @return Request object that should be used to actually fetch data from RRD.
* @throws RrdException In case of JRobin related error (invalid consolidation function or
* invalid time span).
*/
public FetchRequest createFetchRequest(String consolFun, long fetchStart, long fetchEnd,
long resolution) throws RrdException {
return new FetchRequest(this, consolFun, fetchStart, fetchEnd, resolution);
}
/**
* Prepares fetch request to be executed on this RRD. Use returned
* FetchRequest
object and its {@link org.jrobin.core.FetchRequest#fetchData() fetchData()}
* method to actually fetch data from this RRD. Data will be fetched with the smallest
* possible resolution (see RRDTool's
* rrdfetch man page
* for the explanation of the resolution parameter).
*
* @param consolFun Consolidation function to be used in fetch request. Allowed values are
* "AVERAGE", "MIN", "MAX" and "LAST" (these constants are conveniently defined in the
* {@link ConsolFuns} class).
* @param fetchStart Starting timestamp for fetch request.
* @param fetchEnd Ending timestamp for fetch request.
* @return Request object that should be used to actually fetch data from RRD.
* @throws RrdException In case of JRobin related error (invalid consolidation function or
* invalid time span).
*/
public FetchRequest createFetchRequest(String consolFun, long fetchStart, long fetchEnd)
throws RrdException {
return createFetchRequest(consolFun, fetchStart, fetchEnd, 1);
}
synchronized void store(Sample sample) throws IOException, RrdException {
if (closed) {
throw new RrdException("RRD already closed, cannot store this sample");
}
long newTime = sample.getTime();
long lastTime = header.getLastUpdateTime();
if (lastTime >= newTime) {
throw new RrdException("Bad sample timestamp " + newTime +
". Last update time was " + lastTime + ", at least one second step is required");
}
double[] newValues = sample.getValues();
for (int i = 0; i < datasources.length; i++) {
double newValue = newValues[i];
datasources[i].process(newTime, newValue);
}
header.setLastUpdateTime(newTime);
}
synchronized FetchData fetchData(FetchRequest request) throws IOException, RrdException {
if (closed) {
throw new RrdException("RRD already closed, cannot fetch data");
}
Archive archive = findMatchingArchive(request);
return archive.fetchData(request);
}
public Archive findMatchingArchive(FetchRequest request) throws RrdException, IOException {
String consolFun = request.getConsolFun();
long fetchStart = request.getFetchStart();
long fetchEnd = request.getFetchEnd();
long resolution = request.getResolution();
Archive bestFullMatch = null, bestPartialMatch = null;
long bestStepDiff = 0, bestMatch = 0;
for (Archive archive : archives) {
if (archive.getConsolFun().equals(consolFun)) {
long arcStep = archive.getArcStep();
long arcStart = archive.getStartTime() - arcStep;
long arcEnd = archive.getEndTime();
long fullMatch = fetchEnd - fetchStart;
if (arcEnd >= fetchEnd && arcStart <= fetchStart) {
long tmpStepDiff = Math.abs(archive.getArcStep() - resolution);
if (tmpStepDiff < bestStepDiff || bestFullMatch == null) {
bestStepDiff = tmpStepDiff;
bestFullMatch = archive;
}
}
else {
long tmpMatch = fullMatch;
if (arcStart > fetchStart) {
tmpMatch -= (arcStart - fetchStart);
}
if (arcEnd < fetchEnd) {
tmpMatch -= (fetchEnd - arcEnd);
}
if (bestPartialMatch == null || bestMatch < tmpMatch) {
bestPartialMatch = archive;
bestMatch = tmpMatch;
}
}
}
}
if (bestFullMatch != null) {
return bestFullMatch;
}
else if (bestPartialMatch != null) {
return bestPartialMatch;
}
else {
throw new RrdException("RRD file does not contain RRA:" + consolFun + " archive");
}
}
/**
* Finds the archive that best matches to the start time (time period being start-time until now)
* and requested resolution.
*
* @param consolFun Consolidation function of the datasource.
* @param startTime Start time of the time period in seconds.
* @param resolution Requested fetch resolution.
* @return Reference to the best matching archive.
* @throws IOException Thrown in case of I/O related error.
*/
public Archive findStartMatchArchive(String consolFun, long startTime, long resolution) throws IOException {
long arcStep, diff;
int fallBackIndex = 0;
int arcIndex = -1;
long minDiff = Long.MAX_VALUE;
long fallBackDiff = Long.MAX_VALUE;
for (int i = 0; i < archives.length; i++) {
if (archives[i].getConsolFun().equals(consolFun)) {
arcStep = archives[i].getArcStep();
diff = Math.abs(resolution - arcStep);
// Now compare start time, see if this archive encompasses the requested interval
if (startTime >= archives[i].getStartTime()) {
if (diff == 0) // Best possible match either way
{
return archives[i];
}
else if (diff < minDiff) {
minDiff = diff;
arcIndex = i;
}
}
else if (diff < fallBackDiff) {
fallBackDiff = diff;
fallBackIndex = i;
}
}
}
return (arcIndex >= 0 ? archives[arcIndex] : archives[fallBackIndex]);
}
/**
* Returns string representing complete internal RRD state. The returned
* string can be printed to stdout
and/or used for debugging purposes.
*
* @return String representing internal RRD state.
* @throws IOException Thrown in case of I/O related error.
*/
public synchronized String dump() throws IOException {
StringBuffer buffer = new StringBuffer();
buffer.append(header.dump());
for (Datasource datasource : datasources) {
buffer.append(datasource.dump());
}
for (Archive archive : archives) {
buffer.append(archive.dump());
}
return buffer.toString();
}
void archive(Datasource datasource, double value, long numUpdates)
throws IOException, RrdException {
int dsIndex = getDsIndex(datasource.getDsName());
for (Archive archive : archives) {
archive.archive(dsIndex, value, numUpdates);
}
}
/**
* Returns internal index number for the given datasource name. This index is heavily
* used by jrobin.graph package and has no value outside of it.
*
* @param dsName Data source name.
* @return Internal index of the given data source name in this RRD.
* @throws RrdException Thrown in case of JRobin related error (invalid data source name,
* for example)
* @throws IOException Thrown in case of I/O error.
*/
public int getDsIndex(String dsName) throws RrdException, IOException {
for (int i = 0; i < datasources.length; i++) {
if (datasources[i].getDsName().equals(dsName)) {
return i;
}
}
throw new RrdException("Unknown datasource name: " + dsName);
}
/**
* Checks presence of a specific datasource.
*
* @param dsName Datasource name to check
* @return true
if datasource is present in this RRD, false
otherwise
* @throws IOException Thrown in case of I/O error.
*/
public boolean containsDs(String dsName) throws IOException {
for (Datasource datasource : datasources) {
if (datasource.getDsName().equals(dsName)) {
return true;
}
}
return false;
}
Datasource[] getDatasources() {
return datasources;
}
Archive[] getArchives() {
return archives;
}
/**
* Writes the RRD content to OutputStream using XML format. This format
* is fully compatible with RRDTool's XML dump format and can be used for conversion
* purposes or debugging.
*
* @param destination Output stream to receive XML data
* @throws IOException Thrown in case of I/O related error
*/
public synchronized void dumpXml(OutputStream destination) throws IOException {
XmlWriter writer = new XmlWriter(destination);
writer.startTag("rrd");
// dump header
header.appendXml(writer);
// dump datasources
for (Datasource datasource : datasources) {
datasource.appendXml(writer);
}
// dump archives
for (Archive archive : archives) {
archive.appendXml(writer);
}
writer.closeTag();
writer.flush();
}
/**
* This method is just an alias for {@link #dumpXml(OutputStream) dumpXml} method.
*
* @param destination Output stream to receive XML data
* @throws IOException Thrown in case of I/O related error
*/
public synchronized void exportXml(OutputStream destination) throws IOException {
dumpXml(destination);
}
/**
* Returns string representing internal RRD state in XML format. This format
* is fully compatible with RRDTool's XML dump format and can be used for conversion
* purposes or debugging.
*
* @return Internal RRD state in XML format.
* @throws IOException Thrown in case of I/O related error
* @throws RrdException Thrown in case of JRobin specific error
*/
public synchronized String getXml() throws IOException, RrdException {
ByteArrayOutputStream destination = new ByteArrayOutputStream(XML_INITIAL_BUFFER_CAPACITY);
dumpXml(destination);
return destination.toString();
}
/**
* This method is just an alias for {@link #getXml() getXml} method.
*
* @return Internal RRD state in XML format.
* @throws IOException Thrown in case of I/O related error
* @throws RrdException Thrown in case of JRobin specific error
*/
public synchronized String exportXml() throws IOException, RrdException {
return getXml();
}
/**
* Dumps internal RRD state to XML file.
* Use this XML file to convert your JRobin RRD to RRDTool format.
*
* Suppose that you have a JRobin RRD file original.rrd
and you want
* to convert it to RRDTool format. First, execute the following java code:
*
* RrdDb rrd = new RrdDb("original.rrd");
* rrd.dumpXml("original.xml");
*
* Use original.xml
file to create the corresponding RRDTool file
* (from your command line):
*
* rrdtool restore copy.rrd original.xml
*
* @param filename Path to XML file which will be created.
* @throws IOException Thrown in case of I/O related error.
* @throws RrdException Thrown in case of JRobin related error.
*/
public synchronized void dumpXml(String filename) throws IOException, RrdException {
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(filename, false);
dumpXml(outputStream);
}
finally {
if (outputStream != null) {
outputStream.close();
}
}
}
/**
* This method is just an alias for {@link #dumpXml(String) dumpXml(String)} method.
*
* @param filename Path to XML file which will be created.
* @throws IOException Thrown in case of I/O related error
* @throws RrdException Thrown in case of JRobin specific error
*/
public synchronized void exportXml(String filename) throws IOException, RrdException {
dumpXml(filename);
}
/**
* Returns time of last update operation as timestamp (in seconds).
*
* @return Last update time (in seconds).
* @throws IOException Thrown in case of I/O related error
*/
public synchronized long getLastUpdateTime() throws IOException {
return header.getLastUpdateTime();
}
/**
* Returns RRD definition object which can be used to create new RRD
* with the same creation parameters but with no data in it.
*
* Example:
*
*
* RrdDb rrd1 = new RrdDb("original.rrd");
* RrdDef def = rrd1.getRrdDef();
* // fix path
* def.setPath("empty_copy.rrd");
* // create new RRD file
* RrdDb rrd2 = new RrdDb(def);
*
*
* @return RRD definition.
* @throws IOException Thrown in case of I/O related error.
* @throws RrdException Thrown in case of JRobin specific error.
*/
public synchronized RrdDef getRrdDef() throws RrdException, IOException {
// set header
long startTime = header.getLastUpdateTime();
long step = header.getStep();
String path = backend.getPath();
RrdDef rrdDef = new RrdDef(path, startTime, step);
// add datasources
for (Datasource datasource : datasources) {
DsDef dsDef = new DsDef(datasource.getDsName(),
datasource.getDsType(), datasource.getHeartbeat(),
datasource.getMinValue(), datasource.getMaxValue());
rrdDef.addDatasource(dsDef);
}
// add archives
for (Archive archive : archives) {
ArcDef arcDef = new ArcDef(archive.getConsolFun(),
archive.getXff(), archive.getSteps(), archive.getRows());
rrdDef.addArchive(arcDef);
}
return rrdDef;
}
protected void finalize() throws Throwable {
super.finalize();
close();
}
/**
* Copies object's internal state to another RrdDb object.
*
* @param other New RrdDb object to copy state to
* @throws IOException Thrown in case of I/O error
* @throws RrdException Thrown if supplied argument is not a compatible RrdDb object
*/
public synchronized void copyStateTo(RrdUpdater other) throws IOException, RrdException {
if (!(other instanceof RrdDb)) {
throw new RrdException("Cannot copy RrdDb object to " + other.getClass().getName());
}
RrdDb otherRrd = (RrdDb) other;
header.copyStateTo(otherRrd.header);
for (int i = 0; i < datasources.length; i++) {
int j = Util.getMatchingDatasourceIndex(this, i, otherRrd);
if (j >= 0) {
datasources[i].copyStateTo(otherRrd.datasources[j]);
}
}
for (int i = 0; i < archives.length; i++) {
int j = Util.getMatchingArchiveIndex(this, i, otherRrd);
if (j >= 0) {
archives[i].copyStateTo(otherRrd.archives[j]);
}
}
}
/**
* Returns Datasource object corresponding to the given datasource name.
*
* @param dsName Datasource name
* @return Datasource object corresponding to the give datasource name or null
* if not found.
* @throws IOException Thrown in case of I/O error
*/
public Datasource getDatasource(String dsName) throws IOException {
try {
return getDatasource(getDsIndex(dsName));
}
catch (RrdException e) {
return null;
}
}
/**
* Returns index of Archive object with the given consolidation function and the number
* of steps. Exception is thrown if such archive could not be found.
*
* @param consolFun Consolidation function
* @param steps Number of archive steps
* @return Requested Archive object
* @throws IOException Thrown in case of I/O error
* @throws RrdException Thrown if no such archive could be found
*/
public int getArcIndex(String consolFun, int steps) throws RrdException, IOException {
for (int i = 0; i < archives.length; i++) {
if (archives[i].getConsolFun().equals(consolFun) &&
archives[i].getSteps() == steps) {
return i;
}
}
throw new RrdException("Could not find archive " + consolFun + "/" + steps);
}
/**
* Returns Archive object with the given consolidation function and the number
* of steps.
*
* @param consolFun Consolidation function
* @param steps Number of archive steps
* @return Requested Archive object or null if no such archive could be found
* @throws IOException Thrown in case of I/O error
*/
public Archive getArchive(String consolFun, int steps) throws IOException {
try {
return getArchive(getArcIndex(consolFun, steps));
}
catch (RrdException e) {
return null;
}
}
/**
* Returns canonical path to the underlying RRD file. Note that this method makes sense just for
* ordinary RRD files created on the disk - an exception will be thrown for RRD objects created in
* memory or with custom backends.
*
* @return Canonical path to RRD file;
* @throws IOException Thrown in case of I/O error or if the underlying backend is
* not derived from RrdFileBackend.
*/
public String getCanonicalPath() throws IOException {
if (backend instanceof RrdFileBackend) {
return ((RrdFileBackend) backend).getCanonicalPath();
}
else {
throw new IOException("The underlying backend has no canonical path");
}
}
/**
* Returns path to this RRD.
*
* @return Path to this RRD.
*/
public String getPath() {
return backend.getPath();
}
/**
* Returns backend object for this RRD which performs actual I/O operations.
*
* @return RRD backend for this RRD.
*/
public RrdBackend getRrdBackend() {
return backend;
}
/**
* Required to implement RrdUpdater interface. You should never call this method directly.
*
* @return Allocator object
*/
public RrdAllocator getRrdAllocator() {
return allocator;
}
/**
* Returns an array of bytes representing the whole RRD.
*
* @return All RRD bytes
* @throws IOException Thrown in case of I/O related error.
*/
public synchronized byte[] getBytes() throws IOException {
return backend.readAll();
}
/**
* Sets default backend factory to be used. This method is just an alias for
* {@link RrdBackendFactory#setDefaultFactory(java.lang.String)}.
*
* @param factoryName Name of the backend factory to be set as default.
* @throws RrdException Thrown if invalid factory name is supplied, or not called
* before the first backend object (before the first RrdDb object) is created.
*/
public static void setDefaultFactory(String factoryName) throws RrdException {
RrdBackendFactory.setDefaultFactory(factoryName);
}
/**
* Returns an array of last datasource values. The first value in the array corresponds
* to the first datasource defined in the RrdDb and so on.
*
* @return Array of last datasource values
* @throws IOException Thrown in case of I/O error
*/
public synchronized double[] getLastDatasourceValues() throws IOException {
double[] values = new double[datasources.length];
for (int i = 0; i < values.length; i++) {
values[i] = datasources[i].getLastValue();
}
return values;
}
/**
* Returns the last stored value for the given datasource.
*
* @param dsName Datasource name
* @return Last stored value for the given datasource
* @throws IOException Thrown in case of I/O error
* @throws RrdException Thrown if no datasource in this RrdDb matches the given datasource name
*/
public synchronized double getLastDatasourceValue(String dsName) throws IOException, RrdException {
int dsIndex = getDsIndex(dsName);
return datasources[dsIndex].getLastValue();
}
/**
* Returns the number of datasources defined in the file
*
* @return The number of datasources defined in the file
*/
public int getDsCount() {
return datasources.length;
}
/**
* Returns the number of RRA arcihves defined in the file
*
* @return The number of RRA arcihves defined in the file
*/
public int getArcCount() {
return archives.length;
}
/**
* Returns the last time when some of the archives in this RRD was updated. This time is not the
* same as the {@link #getLastUpdateTime()} since RRD file can be updated without updating any of
* the archives.
*
* @return last time when some of the archives in this RRD was updated
* @throws IOException Thrown in case of I/O error
*/
public long getLastArchiveUpdateTime() throws IOException {
long last = 0;
for (Archive archive : archives) {
last = Math.max(last, archive.getEndTime());
}
return last;
}
public synchronized String getInfo() throws IOException {
return header.getInfo();
}
public synchronized void setInfo(String info) throws IOException {
header.setInfo(info);
}
public static void main(String[] args) {
System.out.println("JRobin Java Library :: RRDTool choice for the Java world");
System.out.println("==================================================================");
System.out.println("JRobin base directory: " + Util.getJRobinHomeDirectory());
long time = Util.getTime();
System.out.println("Current timestamp: " + time + ": " + new Date(time * 1000L));
System.out.println("------------------------------------------------------------------");
System.out.println("For the latest information visit: http://www.jrobin.org");
System.out.println("(C) 2003-2005 Sasa Markovic. All rights reserved.");
}
public String toString() {
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()) + "[" + new File(getPath()).getName() + "]";
}
}